bradphelan-sinatras-hat 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +22 -0
  3. data/README.md +235 -0
  4. data/Rakefile +59 -0
  5. data/VERSION +1 -0
  6. data/bradphelan-sinatras-hat.gemspec +145 -0
  7. data/ci.rb +9 -0
  8. data/example/app-with-auth.rb +14 -0
  9. data/example/app-with-cache.rb +30 -0
  10. data/example/app.rb +23 -0
  11. data/example/lib/comment.rb +13 -0
  12. data/example/lib/common.rb +19 -0
  13. data/example/lib/post.rb +16 -0
  14. data/example/simple-app.rb +4 -0
  15. data/example/views/comments/index.erb +6 -0
  16. data/example/views/comments/show.erb +1 -0
  17. data/example/views/posts/index.erb +8 -0
  18. data/example/views/posts/new.erb +11 -0
  19. data/example/views/posts/show.erb +28 -0
  20. data/features/authenticated.feature +12 -0
  21. data/features/create.feature +16 -0
  22. data/features/destroy.feature +18 -0
  23. data/features/edit.feature +17 -0
  24. data/features/formats.feature +19 -0
  25. data/features/headers.feature +28 -0
  26. data/features/index.feature +23 -0
  27. data/features/layouts.feature +11 -0
  28. data/features/nested.feature +20 -0
  29. data/features/new.feature +20 -0
  30. data/features/only.feature +13 -0
  31. data/features/show.feature +31 -0
  32. data/features/steps/authenticated_steps.rb +10 -0
  33. data/features/steps/common_steps.rb +77 -0
  34. data/features/steps/create_steps.rb +21 -0
  35. data/features/steps/destroy_steps.rb +16 -0
  36. data/features/steps/edit_steps.rb +7 -0
  37. data/features/steps/format_steps.rb +11 -0
  38. data/features/steps/header_steps.rb +7 -0
  39. data/features/steps/index_steps.rb +26 -0
  40. data/features/steps/nested_steps.rb +11 -0
  41. data/features/steps/new_steps.rb +15 -0
  42. data/features/steps/only_steps.rb +10 -0
  43. data/features/steps/show_steps.rb +24 -0
  44. data/features/steps/update_steps.rb +22 -0
  45. data/features/support/env.rb +17 -0
  46. data/features/support/views/comments/index.erb +5 -0
  47. data/features/support/views/layout.erb +9 -0
  48. data/features/support/views/people/edit.erb +1 -0
  49. data/features/support/views/people/index.erb +1 -0
  50. data/features/support/views/people/layout.erb +9 -0
  51. data/features/support/views/people/new.erb +1 -0
  52. data/features/support/views/people/show.erb +1 -0
  53. data/features/update.feature +25 -0
  54. data/lib/core_ext/array.rb +5 -0
  55. data/lib/core_ext/hash.rb +23 -0
  56. data/lib/core_ext/module.rb +14 -0
  57. data/lib/core_ext/object.rb +45 -0
  58. data/lib/sinatras-hat.rb +22 -0
  59. data/lib/sinatras-hat/actions.rb +81 -0
  60. data/lib/sinatras-hat/authentication.rb +55 -0
  61. data/lib/sinatras-hat/extendor.rb +24 -0
  62. data/lib/sinatras-hat/hash_mutator.rb +18 -0
  63. data/lib/sinatras-hat/logger.rb +36 -0
  64. data/lib/sinatras-hat/maker.rb +187 -0
  65. data/lib/sinatras-hat/model.rb +110 -0
  66. data/lib/sinatras-hat/resource.rb +57 -0
  67. data/lib/sinatras-hat/responder.rb +106 -0
  68. data/lib/sinatras-hat/response.rb +60 -0
  69. data/lib/sinatras-hat/router.rb +46 -0
  70. data/sinatras-hat.gemspec +34 -0
  71. data/spec/actions/create_spec.rb +68 -0
  72. data/spec/actions/destroy_spec.rb +58 -0
  73. data/spec/actions/edit_spec.rb +52 -0
  74. data/spec/actions/index_spec.rb +72 -0
  75. data/spec/actions/new_spec.rb +39 -0
  76. data/spec/actions/show_spec.rb +85 -0
  77. data/spec/actions/update_spec.rb +83 -0
  78. data/spec/extendor_spec.rb +78 -0
  79. data/spec/fixtures/views/articles/edit.erb +1 -0
  80. data/spec/fixtures/views/articles/index.erb +1 -0
  81. data/spec/fixtures/views/articles/new.erb +1 -0
  82. data/spec/fixtures/views/articles/show.erb +1 -0
  83. data/spec/hash_mutator_spec.rb +23 -0
  84. data/spec/maker_spec.rb +411 -0
  85. data/spec/model_spec.rb +152 -0
  86. data/spec/resource_spec.rb +74 -0
  87. data/spec/responder_spec.rb +139 -0
  88. data/spec/response_spec.rb +120 -0
  89. data/spec/router_spec.rb +105 -0
  90. data/spec/spec_helper.rb +80 -0
  91. metadata +161 -0
@@ -0,0 +1,10 @@
1
+ Given /^I mount the model protecing the show action$/ do
2
+ @app = mock_app do
3
+ set :views, File.join(File.dirname(__FILE__), '..', 'support', 'views')
4
+ # set :logging, true
5
+
6
+ mount Person do
7
+ protect :show
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,77 @@
1
+ Before do
2
+ build_model(:people) do
3
+ string :name
4
+ timestamps
5
+
6
+ has_many :comments
7
+
8
+ validates_presence_of :name
9
+ end
10
+
11
+ build_model(:comments) do
12
+ integer :person_id
13
+ string :name
14
+ timestamps
15
+
16
+ belongs_to :person
17
+ end
18
+
19
+ Person.delete_all
20
+ Comment.delete_all
21
+ end
22
+
23
+ Given /^a model that has a record$/ do
24
+ @record = Person.create! :name => "Pat"
25
+ end
26
+
27
+ Given /^the record has children$/ do
28
+ @not_a_child = Comment.create! :name => "I should never show up!"
29
+ @child_record = @record.comments.create! :name => "Commented!"
30
+ end
31
+
32
+ Given /^a model that does not have a record$/ do
33
+ Person.all.should be_empty
34
+ @record = Person.new
35
+ class << @record
36
+ def to_param
37
+ "230934509834"
38
+ end
39
+ end
40
+ end
41
+
42
+ Given /^a mounted model$/ do
43
+ mock_app do
44
+ set :views, File.join(File.dirname(__FILE__), '..', 'support', 'views')
45
+ # set :logging, true
46
+
47
+ mount Person do
48
+ mount Comment
49
+
50
+ formats[:ruby] = proc { |data| data.inspect }
51
+ end
52
+ end
53
+ end
54
+
55
+ Given /^I mount the model$/ do
56
+ Given "a mounted model"
57
+ end
58
+
59
+ When /^I make a GET request for that record$/ do
60
+ get "/people/#{@record.to_param}"
61
+ end
62
+
63
+ When /^I make a GET request for that record using the '(\w+)' format$/ do |format|
64
+ get "/people/#{@record.to_param}.#{format}"
65
+ end
66
+
67
+ Then /^the body is empty$/ do
68
+ body.should be_empty
69
+ end
70
+
71
+ Then /^the status code is (\d+)$/ do |code|
72
+ response.status.should == code.to_i
73
+ end
74
+
75
+ Then /^I should see "(.*)"$/ do |text|
76
+ body.should =~ /#{text}/
77
+ end
@@ -0,0 +1,21 @@
1
+ When /^I make a POST request with valid form params$/ do
2
+ post "/people?person[name]=Pat"
3
+ end
4
+
5
+ When /^I make a POST request with invalid form params$/ do
6
+ post "/people?person[name]="
7
+ end
8
+
9
+ # When /^I make a POST request with valid serialized attributes for a valid format$/ do
10
+ # post "/people.xml", ({:person => { :name => "Pat" }}).to_xml
11
+ # end
12
+
13
+ Then /^a record is created$/ do
14
+ @record = Person.find_by_name("Pat")
15
+ @record.should_not be_nil
16
+ end
17
+
18
+ Then /^a record is not created$/ do
19
+ @record = Person.find_by_name("Pat")
20
+ @record.should be_nil
21
+ end
@@ -0,0 +1,16 @@
1
+ When /^I make a DELETE request to the path for that record$/ do
2
+ delete "/people/#{@record.to_param}"
3
+ end
4
+
5
+ When /^I make a DELETE request to a path for a non\-existent record$/ do
6
+ delete "/people/345345435"
7
+ end
8
+
9
+ Then /^the record gets destroyed$/ do
10
+ Person.find_by_id(@record.id).should be_nil
11
+ end
12
+
13
+ Then /^I am redirected to the index action$/ do
14
+ response.status.should == 302
15
+ response.location.should == '/people'
16
+ end
@@ -0,0 +1,7 @@
1
+ When /^I get the edit page for that record$/ do
2
+ get "/people/#{@record.to_param}/edit"
3
+ end
4
+
5
+ When /^I get the edit page for a non\-existent record$/ do
6
+ get "/people/23472398732498734/edit"
7
+ end
@@ -0,0 +1,11 @@
1
+ Given /^specify a custom 'ruby' formatter$/ do
2
+ # this is implemented in common_steps.rb
3
+ end
4
+
5
+ Then /^the body is the custom serialized record$/ do
6
+ @response.body.should == @record.inspect
7
+ end
8
+
9
+ Then /^the result should be custom serialized$/ do
10
+ @response.body.should == Person.all.inspect
11
+ end
@@ -0,0 +1,7 @@
1
+ Then /^"(.*)" should be the record "(.*)" time$/ do |header, key|
2
+ DateTime.parse(response.headers[header].to_s).should == @record.send(key)
3
+ end
4
+
5
+ Then /^"(.*)" should be set$/ do |header|
6
+ response.headers[header].should_not be_blank
7
+ end
@@ -0,0 +1,26 @@
1
+ # TODO Use acts_as_fu to build some actual models to test
2
+
3
+ Given /^the model has some records$/ do
4
+ @record = Person.create :name => "Pat"
5
+ @frank = Person.create :name => "Frank"
6
+ end
7
+
8
+ When /^Make a GET request to the index without a format$/ do
9
+ get '/people'
10
+ end
11
+
12
+ When /^Make a GET request to the index with a known format$/ do
13
+ get '/people.xml'
14
+ end
15
+
16
+ When /^Make a GET request to the index using the '(\w+)' format$/ do |format|
17
+ get "/people.#{format}"
18
+ end
19
+
20
+ When /^I make a GET request to the index with an unknown format$/ do
21
+ get '/people.say_wha'
22
+ end
23
+
24
+ Then /^the result should be serialized$/ do
25
+ @response.body.should == Person.all.to_xml
26
+ end
@@ -0,0 +1,11 @@
1
+ When /^Make a GET request to the nested index without a format$/ do
2
+ get "/people/#{@record.id}/comments"
3
+ end
4
+
5
+ When /^Make a GET request to the nested index with a valid format$/ do
6
+ get "/people/#{@record.id}/comments.xml"
7
+ end
8
+
9
+ Then /^the body is the serialized list of children$/ do
10
+ @response.body.should == @record.comments.to_xml
11
+ end
@@ -0,0 +1,15 @@
1
+ When /^I get the new page for that record$/ do
2
+ get '/people/new'
3
+ end
4
+
5
+ When /^I get the new action with a valid format$/ do
6
+ get '/people/new.xml'
7
+ end
8
+
9
+ When /^I get the new action with an invalid format$/ do
10
+ get '/people/new.oops'
11
+ end
12
+
13
+ Then /^the body is a serialized new record$/ do
14
+ body.should == Person.new.to_xml
15
+ end
@@ -0,0 +1,10 @@
1
+ Given /^I mount the model for only the :index action$/ do
2
+ @app = mock_app do
3
+ set :views, File.join(File.dirname(__FILE__), '..', 'support', 'views')
4
+ # set :logging, true
5
+
6
+ mount Person do
7
+ only :index
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # TODO Use acts_as_fu to build some actual models to test
2
+
3
+ When /^I get the show page for that record$/ do
4
+ get "/people/#{@record.to_param}"
5
+ end
6
+
7
+ When /^I get the show page for the non\-existent record$/ do
8
+ get "/people/87345873485763485"
9
+ end
10
+
11
+ When /^I make a request for that record with a format$/ do
12
+ get "/people/#{@record.to_param}.xml"
13
+ end
14
+
15
+ When /^I get the GET request for a non\-existent record with a format$/ do
16
+ get "/people/87345873485763485.xml"
17
+ end
18
+
19
+ Then /^the body is the serialized record$/ do
20
+ body.should == @record.to_xml
21
+ end
22
+
23
+ # Then /^the body is the custom serialized record$/ do
24
+ # end
@@ -0,0 +1,22 @@
1
+ When /^I make a put request with valid form params$/ do
2
+ put "/people/#{@record.to_param}?person[name]=UPDATED"
3
+ end
4
+
5
+ When /^I make a PUT request with invalid form params$/ do
6
+ put "/people/#{@record.to_param}?person[name]="
7
+ end
8
+
9
+ Then /^the record is updated$/ do
10
+ @record.reload
11
+ @record.name.should == "UPDATED"
12
+ end
13
+
14
+ Then /^the response redirects to the record show page$/ do
15
+ response.status.should == 302
16
+ response.location.should == "/people/#{@record.to_param}"
17
+ end
18
+
19
+ Then /^the record is not updated$/ do
20
+ @record.reload
21
+ @record.name.should == "Pat"
22
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. lib sinatras-hat])
2
+
3
+ require 'sinatra/test'
4
+ require 'acts_as_fu'
5
+
6
+ Sinatra::Test.module_eval do
7
+ def mock_app(base=Sinatra::Base, &block)
8
+ @app = Sinatra.new(base, &block)
9
+ end
10
+ end
11
+
12
+ World do
13
+ extend ActsAsFu
14
+ extend Sinatra::Test
15
+ end
16
+
17
+ require 'spec/expectations'
@@ -0,0 +1,5 @@
1
+ The nested view!
2
+
3
+ <% @comments.each do |comment| %>
4
+ <%= comment.name %>
5
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Oh ok!</title>
4
+ </head>
5
+ <body>
6
+ <h1>TEH LAYOUTZ!</h1>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -0,0 +1 @@
1
+ Editing person with id: <%= @person.id %>.
@@ -0,0 +1 @@
1
+ The people: <%= @people.map(&:name).join(', ') %>.
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Oh ok!</title>
4
+ </head>
5
+ <body>
6
+ <h1>TEH LAYOUTZ!</h1>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -0,0 +1 @@
1
+ So, you want to create a new <%= @person.class.name %>?
@@ -0,0 +1 @@
1
+ The person: <%= @person.name %>.
@@ -0,0 +1,25 @@
1
+ Feature: Generating an "update" action
2
+ As a developer
3
+ I want to generate an "update" action
4
+ So that I don't have to manually code it
5
+
6
+ Scenario: A PUT request with valid form params and no format
7
+ Given a model that has a record
8
+ And I mount the model
9
+ When I make a put request with valid form params
10
+ Then the record is updated
11
+ And the response redirects to the record show page
12
+
13
+ Scenario: A PUT request with invalid form params and no format
14
+ Given a model that has a record
15
+ And I mount the model
16
+ When I make a PUT request with invalid form params
17
+ Then I should see "Editing"
18
+ And the record is not updated
19
+
20
+ Scenario: Trying to update a record that does not exist
21
+ Given a model that does not have a record
22
+ And I mount the model
23
+ When I make a put request with valid form params
24
+ Then the status code is 404
25
+ And the body is empty
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def extract_options!
3
+ last.is_a?(Hash) ? pop : { }
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ class Hash
2
+ def make_indifferent!
3
+ keys_values = self.dup
4
+ replace(Hash.new { |h,k| h[k.to_s] if Symbol === k })
5
+ merge!(keys_values)
6
+ end
7
+
8
+ def nest!
9
+ new_params = Hash.new.make_indifferent!
10
+ each_pair do |full_key, value|
11
+ this_param = new_params
12
+ split_keys = full_key.to_s.split(/\]\[|\]|\[/)
13
+ split_keys.each_index do |index|
14
+ break if split_keys.length == index + 1
15
+ this_param[split_keys[index]] ||= Hash.new.make_indifferent!
16
+ this_param = this_param[split_keys[index]]
17
+ end
18
+ this_param[split_keys.last] = value
19
+ end
20
+ clear
21
+ replace(new_params)
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ class Module
2
+ def delegate(*methods)
3
+ options = methods.pop
4
+ raise ArgumentError, "Delegation needs a target." unless options.is_a?(Hash) && to = options[:to]
5
+
6
+ methods.each do |method|
7
+ module_eval(<<-EOS, "(__DELEGATION__)", 1)
8
+ def #{method}(*args, &block)
9
+ #{to}.__send__(#{method.inspect}, *args, &block)
10
+ end
11
+ EOS
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ require 'metaid'
2
+
3
+ class Object
4
+ def tap
5
+ yield self
6
+ self
7
+ end
8
+
9
+ def with(hash)
10
+ hash.each do |key, value|
11
+ meta_def(key) { hash[key] } unless respond_to?(key)
12
+ meta_def("#{key}=") { |v| hash[key] = v } unless respond_to?("#{key}=")
13
+ end
14
+
15
+ return unless block_given?
16
+
17
+ result = yield
18
+
19
+ hash.each do |key, value|
20
+ meta_eval { remove_method(key) }
21
+ meta_eval { remove_method("#{key}=") }
22
+ end
23
+
24
+ result
25
+ end
26
+
27
+ module InstanceExecHelper; end
28
+ include InstanceExecHelper
29
+ def instance_exec(*args, &block)
30
+ begin
31
+ old_critical, Thread.critical = Thread.critical, true
32
+ n = 0
33
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
34
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
35
+ ensure
36
+ Thread.critical = old_critical
37
+ end
38
+ begin
39
+ ret = send(mname, *args)
40
+ ensure
41
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
42
+ end
43
+ ret
44
+ end
45
+ end