AWS S3 using AWS/S3 Ruby gem

working on storing files to Amazon’s S3 web service. Here’s what I’ve come up with so far:

1
2
3
4
5
6
7
8
9
10
11
12
require 'aws/s3'
def aws_connect
  @aws_connect ||= AWS::S3::Base.establish_connection!(
    :access_key_id=>AWS_KEY,
    :secret_access_key=>AWS_SECRET
  )
end
 
def aws_upload(file,bucket=AWS_BUCKET)
  aws_connect
  AWS::S3::S3Object.store(file,open(file),bucket)
end

then a:

1
aws_upload('test.txt')

Will upload the the file to the Amazon bucket. Pretty simple.

Posted: March 22nd, 2010 | Author: jay | Filed under: Code | Tags: , , , , , , , , | No Comments »

Broken chain…

Well, I’ve been working on this thing, but have not making the kind of progress I would like. I blame this on a few things, but mainly we’ve been social (which isn’t really a bad thing)…

Anyway, here’s where things stand:

User

sign-up, email activation, login, password reset, and logout seem to be in order

Oauth

is working on a high level but needs to be cleaned up and better integrated into the rest of the User experience.

Site functionality

in the initial stages, need to (and will) work on that tonight

Posted: March 21st, 2010 | Author: jay | Filed under: Uncategorized | No Comments »

OAuth summarized

Application Scope

1
2
# create the consumer...
consumer ||= OAuth::Consumer.new(KEY, SECRET, {:site => SITE, :authorize_path => PATH })

Session Scope

1
2
3
4
5
# create the request token...
rt=consumer.get_request_token({ :oauth_callback => OAUTH_CALLBACK_URL })
# save the request token and secret in the session...
session[:r_token]=rt.token
session[:r_secret]=rt.secret

User Scope (Model)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# use session values to create the request token...
rt=OAuth::RequestToken.new(consumer, session[:r_token], session[:r_secret])
# grab the user data from the OAuth provider...
access_token=rt.get_access_token({:oauth_verifier=>params[:oauth_verifier]})
oauth_user_json=access_token.get(VERIFY_PATH).body
oauth_user=JSON.parse(oauth_user_json)
# create or find the the user (using twitter.com for the email address - could use some work)... 
u=TwitterUser.first_or_create(:email=>"#{oauth_user['screen_name']}@twitter.com")
u.username=oauth_user['screen_name']
u.save!
u.oauth_tokens.all.destroy
u.oauth_tokens.new(:user_access_token=>access_token.to_yaml)
u.save!
# set the session user for future use...
session[:user]=u.id
...
# and when you need access to the OAuth provider again, use the access_token stored in the User model
u=User.first(:id=>session[:user])
access_token=YAML::load(u.oauth_tokens.first.user_access_token)
verify=access_token.get(OAUTH_PROVIDERS["https://twitter.com"][:verify_path]).body
Posted: March 17th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , , , , , | No Comments »

Storing a Ruby object in Rack::Session will probably clobber the session

In my case it was an OAuth request token and when it was serialized it must have been larger than the cookie limit. Spent too much time on this.

Posted: March 17th, 2010 | Author: jay | Filed under: Code | Tags: , , , | No Comments »

My war is winding down with OAuth

Summary of what’s going on with OAuth:

  1. create a OAuth consumer. Consumer is made up of:
    • application key
    • application secret
    • the url and path to the OAuth provider
  2. create a request token:
    • this makes a call to the provider
    • the request token sends the callback URL to the provider
    • it seems like this expires pretty quickly (at least with Twitter)
    • therefore, I’ll probably hold this in a session
    • once returned from the provider, you can redirect to the provider to complete the access
    • once access is granted, the provider will return the user to the callback URL you sent
  3. create the access token:
    • this makes a call to the provider
    • the user is redirected from the provider to the callback URL
    • a querystring variable – oauth_verifier – is included with the callback URL
    • the oauth_verifier value is then sent back to the provider
    • the provider then returns an access token
    • the access token can hang around for a while and I’ll save that in the database attached to the User model
Posted: March 16th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , , | No Comments »

Going to war with OAuth

And OAuth is winning

Posted: March 15th, 2010 | Author: jay | Filed under: Code | Tags: , | No Comments »

CSS sprites for buttons

Found these login buttons online which have three states – normal, hover and visited. This makes them perfect to use as a CSS sprite, so here’s how that went down:

The CSS looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.signin_button {
  display: block;
  height: 22px;
  width: 150px;
  font-size: .1em;
}
 
#twitter_button {
  background: transparent url(/images/signin_twitter.png) top left no-repeat;	
}
 
#facebook_button {
  background: transparent url(/images/signin_facebook.png) top left no-repeat;	
}
 
#twitter_button:hover, #facebook_button:hover {
  background-position: 0px -24px;
}
 
#twitter_button:active, #facebook_button:active {
  background-position: 0px -48px;
}
 
span.hide {
  display: none;
}

and the HTML looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
  <p>
    <a class='signin_button' href='/oauth/create' id='twitter_button'>
      <span class='hide'>
        Sign in with Twitter.
      </span>
    </a>
    <a class='signin_button' href='/oauth/create' id='facebook_button'>
      <span class='hide'>
        Sign in with Facebook.
      </span>
    </a>
  </p>

Note: display: none; is a no-no in terms of accessibility, but after the first couple accessibility-friendly attempts failed, I got lazy. Perhaps I should pursue a JavaScript solution, as I **believe** that screen readers interrupt the page pre-JS.

Posted: March 14th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »

Haml and Google SMTP for email

I first thought that I would have to use ERB to render non-HTML-like templates. Not true. Haml let you do a :plain filter which is pretty much the same as using ERB templates.

Here’s a simple example haml template for email:

1
2
3
4
5
6
:plain
  Welcome #{@user.username} to Fund-A-Stache! 
 
  click the following link to activate your account:
 
  #{activation_link(@user)}

I’m also using Google SMTP for email. That way I can send email from my home (which blocks SMTP traffic). This may change, but just so I can remember why I’m doing this, here’s the Pony call:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Pony.mail(
  :to=>@user.email, 
  :from=>'user@example.org', 
  :subject=>'Welcome', 
  :body=>(haml :registration_email, :layout=>false),
  :content_type=>'text/html',
  :via=>:smtp,
  :smtp=>{
    :host=>'smtp.gmail.com',
    :port=>'587',
    :tls=>true,
    :user=>'user@example.org',
    :password=>'secret',
    :auth=>:plain, # :plain, :login, :cram_md5, no auth by default
    :domain=>"example.org" # the HELO domain provided by the client to the server
  }
)
Posted: March 13th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , , | No Comments »

Taking a pass on URLs that aren’t supposed to exist (in Sinatra)

This is simple, but I’m dumping it here anyway… I’d like to use the Twitter model of account access: http://example.com/USERNAME

So, if the user exists, if should display that user’s page. If the user doesn’t exist (or the user is deactivated) then this route will pass, which in this case, passes to the 404 error page. Here’s how I’m dealing with that in my routes:

1
2
3
4
5
    get '/:username' do
      @user=User.first(:username=>params[:username],:activated=>true)
      pass unless @user
      haml :user_page
    end

Note:This should be low on the chain of routes so that users with names or actual routes can’t be rendered as user pages. Example, a user with a username of “login” should not interfere with the application function of “login”. I might look further into protecting this beyond it’s position in the URL parsing chain.

Posted: March 13th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »

Setting a domain name (env['HTTP_HOST']) to use in Rack::Test

This may not be the best way to do this, but this is what I used at the top of my test file so that a helper method has a value for Rack’s env['HTTP_HOST']:

1
2
3
4
5
6
7
class FundastacheUserTest < Test::Unit::TestCase
  ...
  def env
    last_request.env['HTTP_HOST']="example.org"
    last_request.env
  end
  ...

The test tests to see that using the activation link (that a user gets in an email) activates the user. It looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
  def test_using_activation_link_should_activate_account
    create_user
    user=User.first
    path=activation_link(user)
    path=~/http\:\/\/example.org(.*)/
    get $1
    follow_redirect!
    assert last_response.ok?
    assert last_response.body.include?('Account activated!')
    user=User.first
    assert user.activated
  end

and the helper method looks something like this:

1
2
3
4
5
6
7
8
9
10
 
module FundAStache
  module Helpers    
    ...
    def activation_link(user)
      "http://#{env['HTTP_HOST']}/user/activate/#{user.activation_token}"
    end
    ...
  end
end
Posted: March 13th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »