atmos-singem 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.md +36 -0
- data/Rakefile +63 -0
- data/TODO +1 -0
- data/bin/singem +26 -0
- data/lib/generators/cucumber/USAGE +5 -0
- data/lib/generators/cucumber/cucumber_generator.rb +82 -0
- data/lib/generators/cucumber/templates/LICENSE +20 -0
- data/lib/generators/cucumber/templates/README.md +4 -0
- data/lib/generators/cucumber/templates/Rakefile +73 -0
- data/lib/generators/cucumber/templates/TODO +4 -0
- data/lib/generators/cucumber/templates/config.ru.erb +11 -0
- data/lib/generators/cucumber/templates/features/basics.feature.erb +6 -0
- data/lib/generators/cucumber/templates/features/step_definitions/basics.rb.erb +8 -0
- data/lib/generators/cucumber/templates/features/support/env.rb.erb +32 -0
- data/lib/generators/cucumber/templates/lib/templates.rb.erb +9 -0
- data/lib/generators/cucumber/templates/lib/templates/app.rb.erb +7 -0
- data/lib/generators/cucumber/templates/spec/fixtures.rb.erb +0 -0
- data/lib/generators/cucumber/templates/spec/spec_helper.rb.erb +40 -0
- data/lib/generators/cucumber/templates/spec/templates_spec.rb.erb +8 -0
- data/lib/generators/singem/USAGE +5 -0
- data/lib/generators/singem/singem_generator.rb +77 -0
- data/lib/generators/singem/templates/LICENSE +20 -0
- data/lib/generators/singem/templates/README.md +4 -0
- data/lib/generators/singem/templates/Rakefile +66 -0
- data/lib/generators/singem/templates/TODO +4 -0
- data/lib/generators/singem/templates/config.ru.erb +11 -0
- data/lib/generators/singem/templates/lib/templates.rb.erb +9 -0
- data/lib/generators/singem/templates/lib/templates/app.rb.erb +7 -0
- data/lib/generators/singem/templates/spec/fixtures.rb.erb +0 -0
- data/lib/generators/singem/templates/spec/spec_helper.rb.erb +40 -0
- data/lib/generators/singem/templates/spec/templates_spec.rb.erb +8 -0
- data/lib/generators/twitter/USAGE +5 -0
- data/lib/generators/twitter/templates/LICENSE +20 -0
- data/lib/generators/twitter/templates/README.md +4 -0
- data/lib/generators/twitter/templates/Rakefile +72 -0
- data/lib/generators/twitter/templates/TODO +4 -0
- data/lib/generators/twitter/templates/config.ru.erb +14 -0
- data/lib/generators/twitter/templates/features/adding_items_from_the_bookmarklet.feature +16 -0
- data/lib/generators/twitter/templates/features/adding_items_to_my_wishlist.feature +19 -0
- data/lib/generators/twitter/templates/features/basics.feature.erb +11 -0
- data/lib/generators/twitter/templates/features/step_definitions/basics.rb.erb +15 -0
- data/lib/generators/twitter/templates/features/support/env.rb.erb +20 -0
- data/lib/generators/twitter/templates/features/viewing_my_wish_list.feature +14 -0
- data/lib/generators/twitter/templates/features/viewing_the_site_for_the_first_time.feature +6 -0
- data/lib/generators/twitter/templates/lib/templates.rb.erb +56 -0
- data/lib/generators/twitter/templates/lib/templates/models/user.rb.erb +38 -0
- data/lib/generators/twitter/templates/lib/templates/sinatra/app.rb.erb +66 -0
- data/lib/generators/twitter/templates/lib/templates/views/about.haml +5 -0
- data/lib/generators/twitter/templates/lib/templates/views/failed.haml +4 -0
- data/lib/generators/twitter/templates/lib/templates/views/home.haml +1 -0
- data/lib/generators/twitter/templates/spec/fixtures.rb.erb +0 -0
- data/lib/generators/twitter/templates/spec/helpers.rb.erb +43 -0
- data/lib/generators/twitter/templates/spec/spec_helper.rb.erb +38 -0
- data/lib/generators/twitter/templates/spec/templates_spec.rb.erb +59 -0
- data/lib/generators/twitter/twitter_generator.rb +90 -0
- data/lib/singen.rb +0 -0
- data/spec/singem_spec.rb +7 -0
- data/spec/spec_helper.rb +2 -0
- metadata +196 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Feature: Visiting the Home Page
|
|
2
|
+
In order to display a splash page
|
|
3
|
+
|
|
4
|
+
Scenario: Seeing the splash page unauthenticated
|
|
5
|
+
When I visit the home page
|
|
6
|
+
Then I should be prompted to try the app
|
|
7
|
+
|
|
8
|
+
Scenario: Seeing the splash page authenticated
|
|
9
|
+
Given I am logged in
|
|
10
|
+
When I visit the home page
|
|
11
|
+
Then I should be greeted
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
When /^I visit the home page$/ do
|
|
2
|
+
get '/'
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
Then /^I should be greeted$/ do
|
|
6
|
+
last_response.should have_selector("h2:contains('Hello There, you little twitterer!')")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Then /^I should be prompted to try the app$/ do
|
|
10
|
+
last_response.should have_selector("p:contains('Would you mind trying out my new app')")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Given /^I am logged in$/ do
|
|
14
|
+
login_quentin
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
require 'spec/mocks'
|
|
3
|
+
require 'webrat'
|
|
4
|
+
require 'rack/test'
|
|
5
|
+
require 'dm-sweatshop'
|
|
6
|
+
require 'pp'
|
|
7
|
+
|
|
8
|
+
require File.dirname(__FILE__)+'/../../lib/<%= name %>'
|
|
9
|
+
require File.dirname(__FILE__)+'/../../spec/helpers'
|
|
10
|
+
|
|
11
|
+
World(Rack::Test::Methods)
|
|
12
|
+
World(Spec::Mocks::ExampleMethods)
|
|
13
|
+
World(Webrat::Methods)
|
|
14
|
+
World(Webrat::Matchers)
|
|
15
|
+
World(<%= name.camelize %>::AppHelpers)
|
|
16
|
+
|
|
17
|
+
Before do
|
|
18
|
+
DataMapper.setup(:default, "sqlite3://:memory:")
|
|
19
|
+
DataMapper.auto_migrate!
|
|
20
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Feature: Viewing my wish list
|
|
2
|
+
In order to display a users' preferences
|
|
3
|
+
As a twitter user
|
|
4
|
+
|
|
5
|
+
Background:
|
|
6
|
+
Given I am logged in
|
|
7
|
+
And I have created a "53cm Pistas" category
|
|
8
|
+
|
|
9
|
+
Scenario: After Authenticating
|
|
10
|
+
When I visit the home page
|
|
11
|
+
Then I should see my list of categories
|
|
12
|
+
When I enter "Prostitutes" for a new category
|
|
13
|
+
And I click submit
|
|
14
|
+
Then I should see the "Prostitutes" list
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
gem 'oauth'
|
|
2
|
+
require 'oauth'
|
|
3
|
+
gem 'json'
|
|
4
|
+
require 'json'
|
|
5
|
+
gem 'haml', '~>2.0.9'
|
|
6
|
+
require 'haml/util'
|
|
7
|
+
require 'haml/engine'
|
|
8
|
+
gem 'curb'
|
|
9
|
+
require 'curb'
|
|
10
|
+
require 'logger'
|
|
11
|
+
|
|
12
|
+
gem 'data_objects', '~>0.9.11'
|
|
13
|
+
gem 'dm-core', '~>0.9.10'
|
|
14
|
+
gem 'dm-types', '~>0.9.10'
|
|
15
|
+
gem 'dm-validations', '~>0.9.10'
|
|
16
|
+
gem 'dm-timestamps', '~>0.9.10'
|
|
17
|
+
require 'dm-core'
|
|
18
|
+
require 'dm-types'
|
|
19
|
+
require 'dm-validations'
|
|
20
|
+
require 'dm-timestamps'
|
|
21
|
+
require 'sinatra/base'
|
|
22
|
+
|
|
23
|
+
module <%= name.camelize %>
|
|
24
|
+
module Log
|
|
25
|
+
def self.logger
|
|
26
|
+
if @logger.nil?
|
|
27
|
+
@logger = Logger.new("<%= name %>.log")
|
|
28
|
+
@logger.level = Logger::INFO
|
|
29
|
+
end
|
|
30
|
+
@logger
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module OAuth
|
|
35
|
+
def self.consumer
|
|
36
|
+
::OAuth::Consumer.new(ENV['<%= name.upcase %>_READKEY'],
|
|
37
|
+
ENV['<%= name.upcase %>_READSECRET'],
|
|
38
|
+
{:site => 'http://twitter.com'})
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.retryable(options = {}, &block)
|
|
43
|
+
opts = { :tries => 1, :on => StandardError }.merge(options)
|
|
44
|
+
retry_exception, retries = opts[:on], opts[:tries]
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
return yield
|
|
48
|
+
rescue retry_exception
|
|
49
|
+
retry if (retries -= 1) > 0
|
|
50
|
+
end
|
|
51
|
+
yield
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
require File.dirname(__FILE__)+'/<%= name %>/models/user'
|
|
56
|
+
require File.dirname(__FILE__)+'/<%= name %>/sinatra/app'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module <%= name.camelize %>
|
|
2
|
+
class User
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
class UserCreationError < StandardError; end
|
|
5
|
+
storage_names[:default] = '<%= name.camelize %>_users'
|
|
6
|
+
|
|
7
|
+
property :id, Serial
|
|
8
|
+
property :twitter_id, Integer, :nullable => false, :unique => true
|
|
9
|
+
property :name, String
|
|
10
|
+
property :token, String
|
|
11
|
+
property :secret, String
|
|
12
|
+
|
|
13
|
+
property :url, String, :length => 512
|
|
14
|
+
property :avatar, String, :length => 512, :default => 'http://static.twitter.com/images/default_profile_normal.png'
|
|
15
|
+
|
|
16
|
+
timestamps :at
|
|
17
|
+
|
|
18
|
+
def access_token
|
|
19
|
+
::OAuth::AccessToken.new(::<%= name.camelize %>::OAuth.consumer, token, secret)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.create_twitter_user(twitter_id)
|
|
23
|
+
content = Curl::Easy.perform("http://twitter.com/users/show/#{twitter_id}.json") do |curl|
|
|
24
|
+
curl.timeout = 30
|
|
25
|
+
end
|
|
26
|
+
user_info = JSON.parse(content.body_str)
|
|
27
|
+
raise UserCreationError.new("Unable to find '#{twitter_id}'") if(user_info['error'] == 'Not found')
|
|
28
|
+
result = unless user_info['error']
|
|
29
|
+
self.first_or_create({:twitter_id => user_info['id']},{
|
|
30
|
+
:name => user_info['name'],
|
|
31
|
+
:avatar => user_info['profile_image_url'],
|
|
32
|
+
:url => 'http://twitter.com/'+user_info['screen_name']})
|
|
33
|
+
end
|
|
34
|
+
rescue JSON::ParserError
|
|
35
|
+
raise UserCreationError.new("Unable to find '#{twitter_id}'")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module <%= name.camelize %>
|
|
2
|
+
class App < Sinatra::Default
|
|
3
|
+
set :views, File.expand_path(File.dirname(__FILE__)+'/../views')
|
|
4
|
+
enable :sessions
|
|
5
|
+
enable :methodoverride
|
|
6
|
+
|
|
7
|
+
helpers do
|
|
8
|
+
def oauth_consumer
|
|
9
|
+
::<%= name.camelize %>::OAuth.consumer
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def current_user
|
|
13
|
+
session[:user_id].nil? ? nil : ::<%= name.camelize %>::User.get(session[:user_id])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
error do
|
|
18
|
+
<%= name.camelize %>::Log.logger.info env['sinatra.error'].message
|
|
19
|
+
<%= name.camelize %>::Log.logger.info env['sinatra.error'].backtrace.join("\n")
|
|
20
|
+
haml :failed
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
get '/' do
|
|
24
|
+
haml current_user.nil? ? :about : :home
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
get '/authenticate' do
|
|
28
|
+
::<%= name.camelize %>.retryable(:times => 2) do
|
|
29
|
+
request_token = oauth_consumer.get_request_token
|
|
30
|
+
session[:request_token] = request_token.token
|
|
31
|
+
session[:request_token_secret] = request_token.secret
|
|
32
|
+
redirect request_token.authorize_url
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
get '/peace' do
|
|
37
|
+
session.clear
|
|
38
|
+
redirect '/'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
get '/callback' do
|
|
42
|
+
@request_token = ::OAuth::RequestToken.new(oauth_consumer,
|
|
43
|
+
session[:request_token],
|
|
44
|
+
session[:request_token_secret])
|
|
45
|
+
|
|
46
|
+
access_token = @request_token.get_access_token
|
|
47
|
+
|
|
48
|
+
oauth_response = oauth_consumer.request(:get, '/account/verify_credentials.json',
|
|
49
|
+
access_token, { :scheme => :query_string })
|
|
50
|
+
case oauth_response
|
|
51
|
+
when Net::HTTPSuccess
|
|
52
|
+
user_info = JSON.parse(oauth_response.body)
|
|
53
|
+
user = ::<%= name.camelize %>::User.first_or_create(:twitter_id => user_info['id']) # really wish first_or_create behaved sanely
|
|
54
|
+
user.name, user.avatar = user_info['name'], user_info['profile_image_url']
|
|
55
|
+
user.token, user.secret = access_token.token, access_token.secret
|
|
56
|
+
user.url = 'http://twitter.com/'+user_info['screen_name']
|
|
57
|
+
user.save
|
|
58
|
+
|
|
59
|
+
session[:user_id] = user.id
|
|
60
|
+
redirect '/'
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError.new('Unhandled HTTP Response')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
%h2 Hello There, you little twitterer!
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class Net::HTTPResponse
|
|
2
|
+
def body=(content)
|
|
3
|
+
@body = content
|
|
4
|
+
@read = true
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Webrat.configure do |config|
|
|
9
|
+
config.mode = :rack_test
|
|
10
|
+
config.application_port = 4567
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module <%= name.camelize %>::AppHelpers
|
|
14
|
+
def app
|
|
15
|
+
@app = Rack::Builder.new do
|
|
16
|
+
run <%= name.camelize %>::App
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def login_quentin
|
|
21
|
+
response = Net::HTTPSuccess.new('1.0', 200, nil)
|
|
22
|
+
response.body = "{\"description\":\"lulz\",\"profile_background_image_url\":\"http:\\/\\/static.twitter.com\\/images\\/themes\\/theme3\\/bg.gif\",\"utc_offset\":-25200,\"friends_count\":157,\"profile_background_color\":\"EDECE9\",\"profile_text_color\":\"634047\",\"url\":\"http:\\/\\/example.org\",\"name\":\"Quentin Blake\",\"favourites_count\":6,\"profile_link_color\":\"088253\",\"protected\":false,\"status\":{\"truncated\":false,\"in_reply_to_status_id\":null,\"text\":\"stu stu studio\",\"in_reply_to_user_id\":null,\"favorited\":false,\"created_at\":\"Tue Mar 31 19:02:12 +0000 2009\",\"id\":1426242614,\"source\":\"<a href=\\\"http:\\/\\/iconfactory.com\\/software\\/twitterrific\\\">twitterrific<\\/a>\"},\"created_at\":\"Sun Mar 18 20:07:13 +0000 2007\",\"statuses_count\":2560,\"profile_background_tile\":false,\"time_zone\":\"Mountain Time (US & Canada)\",\"profile_sidebar_fill_color\":\"E3E2DE\",\"profile_image_url\":\"http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\",\"notifications\":false,\"profile_sidebar_border_color\":\"D3D2CF\",\"location\":\"Boulder, Colorado\",\"id\":1484261,\"following\":false,\"followers_count\":368,\"screen_name\":\"caboose\"}"
|
|
23
|
+
login(response)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def unauthorized_quentin
|
|
27
|
+
response = Net::HTTPUnauthorized.new('1.0', 401, nil)
|
|
28
|
+
response.body = "Unauthorized"
|
|
29
|
+
lambda { login(response) }.should raise_error(ArgumentError)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def login(response)
|
|
33
|
+
token = 'oU5W1XD2TTZhWT6Snfii9JbVBUkJOurCKhWQHz98765'
|
|
34
|
+
|
|
35
|
+
consumer = mock('Consumer', {:request => response})
|
|
36
|
+
request_token = mock('RequestToken', {:get_access_token => mock('AccessToken', {:token => 'foo', :secret => 'bar'})})
|
|
37
|
+
|
|
38
|
+
OAuth::Consumer.stub!(:new).and_return(consumer)
|
|
39
|
+
OAuth::RequestToken.stub!(:new).and_return(request_token)
|
|
40
|
+
|
|
41
|
+
visit "/callback?oauth_token=#{token}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
$TESTING=true
|
|
2
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'randexp'
|
|
5
|
+
require '<%= name %>'
|
|
6
|
+
require 'rack/test'
|
|
7
|
+
require 'webrat'
|
|
8
|
+
require 'dm-sweatshop'
|
|
9
|
+
gem 'fakeweb', '~>1.2.4'
|
|
10
|
+
require 'fakeweb'
|
|
11
|
+
require 'pp'
|
|
12
|
+
|
|
13
|
+
FakeWeb.allow_net_connect = false
|
|
14
|
+
ENV['<%= name.upcase %>_READKEY'] = /\w{18}/.gen
|
|
15
|
+
ENV['<%= name.upcase %>_READSECRET'] = /\w{18}/.gen
|
|
16
|
+
|
|
17
|
+
require File.dirname(__FILE__)+'/fixtures'
|
|
18
|
+
require File.dirname(__FILE__)+'/helpers'
|
|
19
|
+
|
|
20
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
|
21
|
+
|
|
22
|
+
Spec::Runner.configure do |config|
|
|
23
|
+
config.include(Rack::Test::Methods)
|
|
24
|
+
config.include(Webrat::Methods)
|
|
25
|
+
config.include(Webrat::Matchers)
|
|
26
|
+
config.include(<%= name.camelize %>::AppHelpers)
|
|
27
|
+
|
|
28
|
+
config.before(:each) do
|
|
29
|
+
::<%= name.camelize %>::App.environment = :development
|
|
30
|
+
DataMapper.auto_migrate!
|
|
31
|
+
FakeWeb.clean_registry
|
|
32
|
+
FakeWeb.register_uri(:post, "http://twitter.com:80/oauth/request_token",
|
|
33
|
+
[{:body => "oauth_token=requestkey&oauth_token_secret=requestsecret", :status => ["200", "OK"]},
|
|
34
|
+
{:body => "", :status => ["401", "Unauthorized"]},
|
|
35
|
+
{:body => "", :status => ["403", "Forbidden"]},
|
|
36
|
+
{:body => "Bad Gateway", :status => ["502", "Bad Gateway"]} ])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "visiting /" do
|
|
4
|
+
describe "when unauthenticated" do
|
|
5
|
+
it "should display the about page" do
|
|
6
|
+
get '/'
|
|
7
|
+
last_response.should have_selector("h2:contains('Do you use twitter?')")
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
describe "when unauthorized" do
|
|
11
|
+
it "should raise an error" do
|
|
12
|
+
lambda { unauthorized_quentin }.should raise_error
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
describe "when authenticated" do
|
|
16
|
+
it "should display a greeting" do
|
|
17
|
+
login_quentin
|
|
18
|
+
get '/'
|
|
19
|
+
last_response.should have_selector("h2:contains('Hello There, you little twitterer!')")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "visiting /peace" do
|
|
25
|
+
describe "when authenticated" do
|
|
26
|
+
it "should clear the session and redirect to the home page" do
|
|
27
|
+
login_quentin
|
|
28
|
+
get '/peace'
|
|
29
|
+
last_response.headers['Location'].should eql('/')
|
|
30
|
+
|
|
31
|
+
get last_response.headers['Location']
|
|
32
|
+
last_response.should have_selector("h2:contains('Do you use twitter?')")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "triggering a 404" do
|
|
38
|
+
it "should display the failed page" do
|
|
39
|
+
pending
|
|
40
|
+
get '/lost'
|
|
41
|
+
last_response.should have_selector("p:contains('Something went wonky.')")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "visiting /authenticate" do
|
|
46
|
+
it "should redirect to twitter for authentication" do
|
|
47
|
+
get '/authenticate'
|
|
48
|
+
last_response.headers['Location'].should eql('http://twitter.com/oauth/authorize?oauth_token=requestkey')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "<%= name.camelize %>::User" do
|
|
53
|
+
it "can create users from twitter ids that exist" do
|
|
54
|
+
lambda { <%= name.camelize %>::User.create_twitter_user('atmos') }.should_not raise_error
|
|
55
|
+
end
|
|
56
|
+
it "can't create users from twitter ids that do not exist" do
|
|
57
|
+
lambda { <%= name.camelize %>::User.create_twitter_user(/\w{18}/.gen) }.should raise_error(<%= name.camelize %>::User::UserCreationError)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
class TwitterGenerator < RubiGen::Base
|
|
2
|
+
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
|
|
3
|
+
Config::CONFIG['ruby_install_name'])
|
|
4
|
+
|
|
5
|
+
default_options :author => nil
|
|
6
|
+
attr_reader :name
|
|
7
|
+
|
|
8
|
+
def initialize(runtime_args, runtime_options = {})
|
|
9
|
+
super
|
|
10
|
+
usage if args.empty?
|
|
11
|
+
@destination_root = File.expand_path(args.shift)
|
|
12
|
+
@name = base_name
|
|
13
|
+
extract_options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def manifest
|
|
17
|
+
record do |m|
|
|
18
|
+
# Ensure appropriate folder(s) exists
|
|
19
|
+
m.directory ''
|
|
20
|
+
BASEDIRS.each { |path| m.directory path }
|
|
21
|
+
m.directory "lib/#{name}"
|
|
22
|
+
m.directory "features/support"
|
|
23
|
+
m.directory "features/step_definitions"
|
|
24
|
+
m.directory "lib/#{name}/sinatra"
|
|
25
|
+
m.directory "lib/#{name}/models"
|
|
26
|
+
m.directory "lib/#{name}/views"
|
|
27
|
+
|
|
28
|
+
# Create stubs
|
|
29
|
+
m.template "config.ru.erb", "config.ru.example"
|
|
30
|
+
m.template "lib/templates.rb.erb", "lib/#{name}.rb"
|
|
31
|
+
m.template "lib/templates/sinatra/app.rb.erb", "lib/#{name}/sinatra/app.rb"
|
|
32
|
+
m.template "lib/templates/models/user.rb.erb", "lib/#{name}/models/user.rb"
|
|
33
|
+
|
|
34
|
+
# cucumber stubs
|
|
35
|
+
m.template "features/support/env.rb.erb", "features/support/env.rb"
|
|
36
|
+
m.template "features/basics.feature.erb", "features/#{name}.feature"
|
|
37
|
+
m.template "features/step_definitions/basics.rb.erb", "features/step_definitions/#{name}.rb"
|
|
38
|
+
|
|
39
|
+
# rspec stubs
|
|
40
|
+
m.template "spec/spec_helper.rb.erb", "spec/spec_helper.rb"
|
|
41
|
+
m.template "spec/helpers.rb.erb", "spec/helpers.rb"
|
|
42
|
+
m.template "spec/templates_spec.rb.erb", "spec/#{name}_spec.rb"
|
|
43
|
+
m.template "spec/fixtures.rb.erb", "spec/fixtures.rb"
|
|
44
|
+
|
|
45
|
+
%w(LICENSE Rakefile README.md).each do |file|
|
|
46
|
+
m.template file, file
|
|
47
|
+
end
|
|
48
|
+
%w(TODO).each do |file|
|
|
49
|
+
m.file file, file
|
|
50
|
+
end
|
|
51
|
+
%w(about failed home).each do |file|
|
|
52
|
+
m.file "lib/templates/views/#{file}.haml", "lib/#{name}/views/#{file}.haml"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
protected
|
|
58
|
+
def banner
|
|
59
|
+
<<-EOS
|
|
60
|
+
Creates a simple RubyGems scaffold.
|
|
61
|
+
|
|
62
|
+
USAGE: #{spec.name} name --simple"
|
|
63
|
+
EOS
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def add_options!(opts)
|
|
67
|
+
opts.separator ''
|
|
68
|
+
opts.separator 'Options:'
|
|
69
|
+
# For each option below, place the default
|
|
70
|
+
# at the top of the file next to "default_options"
|
|
71
|
+
# opts.on("-a", "--author=\"Your Name\"", String,
|
|
72
|
+
# "Some comment about this option",
|
|
73
|
+
# "Default: none") { |x| options[:author] = x }
|
|
74
|
+
opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def extract_options
|
|
78
|
+
# for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
|
|
79
|
+
# Templates can access these value via the attr_reader-generated methods, but not the
|
|
80
|
+
# raw instance variable value.
|
|
81
|
+
# @author = options[:author]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Installation skeleton. Intermediate directories are automatically
|
|
85
|
+
# created so don't sweat their absence here.
|
|
86
|
+
BASEDIRS = %w(
|
|
87
|
+
lib
|
|
88
|
+
spec
|
|
89
|
+
)
|
|
90
|
+
end
|