david-merb_resourceful 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 David Leal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,67 @@
1
+ h1. merb_resourceful
2
+
3
+ merb_resourceful is a plugin similar to resource_controller or make_resourceful for rails.
4
+
5
+ h2. Usage
6
+
7
+ Generate a new resource (this will change in the future)
8
+
9
+ merb-gen resource my_resource
10
+
11
+ Edit app/controllers/my_resources.rb and leave the class declaration only
12
+
13
+ class MyResources < Merb::Controller
14
+ resourceful
15
+ end
16
+
17
+ This gives you a basic controller with a few defaults (destroy isn't implemented yet).
18
+
19
+ h3. Nested resources
20
+
21
+ class MyResources < Merb::Controller
22
+ resourceful :belongs_to => :my_parent
23
+ # The children will be accessed through parent.[children association]
24
+ # E.g. my_parent.my_resources
25
+ # this will be configurable in the future
26
+ def my_parent
27
+ @foo ||= session.user.parents.get(params[:foo_id])
28
+ end
29
+ end
30
+
31
+ h3. Scoped resources
32
+
33
+ class MyResources < Merb::Controller
34
+ resourceful :scope => :scope_method
35
+ # works just like :parent, but doesn't use @bar for routing
36
+ def scope_method
37
+ @bar ||= session.user.parents.get(params[:bar_id])
38
+ end
39
+ end
40
+
41
+ h3. Per-action settings
42
+
43
+ class MyResources < Merb::Controller
44
+ resourceful do
45
+ index :layout => 'my_special_layout'
46
+ create :scope => :scope_for_create
47
+ end
48
+ def scope_for_create
49
+ @baz = get_me!
50
+ end
51
+ end
52
+
53
+ h3. Param injection
54
+
55
+ Merb already supports this with the defer_to method in the router, but I think this is way more explicit.
56
+
57
+ class MyResources < Merb::Controller
58
+ resourceful do
59
+ index :params => lambda {{ :creator => session.user }} # passes :creator as a param to MyResource.new
60
+ create :params => lambda {{ :creator => session.user }} # passes :creator as a param to the resource's update method
61
+ end
62
+ end
63
+
64
+ h2. Lots more to come
65
+
66
+ * More docs
67
+ * Implement destroy
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "merb_resourceful"
8
+ GEM_VERSION = "0.0.1"
9
+ AUTHOR = "David Leal"
10
+ EMAIL = "dgleal@gmail.com"
11
+ HOMEPAGE = "http://github.com/david/merb_resourceful"
12
+ SUMMARY = "merb_resourceful lends a little magic to common resource actions."
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.rubyforge_project = 'merb'
16
+ s.name = GEM_NAME
17
+ s.version = GEM_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README.textile", "LICENSE", 'TODO']
21
+ s.summary = SUMMARY
22
+ s.description = s.summary
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+ s.add_dependency('merb', '>= 1.0')
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE README.textile Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
29
+
30
+ end
31
+
32
+ Rake::GemPackageTask.new(spec) do |pkg|
33
+ pkg.gem_spec = spec
34
+ end
35
+
36
+ desc "install the plugin as a gem"
37
+ task :install do
38
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
39
+ end
40
+
41
+ desc "Uninstall the gem"
42
+ task :uninstall do
43
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
44
+ end
45
+
46
+ desc "Create a gemspec file"
47
+ task :gemspec do
48
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
49
+ file.puts spec.to_ruby
50
+ end
51
+ end
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ TODO:
2
+ * Rewrite the tests using a sample merb app.
3
+ * Validate options
4
+ * Cleanup internal methods, so they can be overriden by the user
5
+ * Make sure all options make sense.
6
+ * Add passing a symbol to some options, besides lambdas, and that will call the corresponding method
7
+ * Documentation.
@@ -0,0 +1,280 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'controller'))
2
+
3
+
4
+ module Merb
5
+ module Plugins
6
+ module Resourceful
7
+ class Builder
8
+ ALL_ACTIONS = %w(index show new create edit update destroy)
9
+
10
+ def initialize(controller_class, options)
11
+ @controller_class = controller_class
12
+ @options = options
13
+ @resource_class = @controller_class.name.demodulize.singularize
14
+ @resource_plural = @controller_class.name.demodulize.underscore
15
+ @resource_name = @resource_plural.singularize
16
+
17
+ end
18
+
19
+ def build(&block)
20
+ @controller_class.send :include, ::Merb::Plugins::Resourceful::Controller
21
+ @controller_class.const_set('RESOURCE_NAME', @resource_name)
22
+ @controller_class.const_set('RESOURCES_NAME', @resource_plural)
23
+
24
+ def_source
25
+
26
+ ALL_ACTIONS.each { |action| send(action) }
27
+
28
+ @controller_class.class_eval <<-EOF
29
+ def resources_set(r)
30
+ @#{@resource_plural} = r
31
+ end
32
+
33
+ def resource_set(r)
34
+ @#{@resource_name} = r
35
+ end
36
+
37
+ def resource_ivar
38
+ @#{@resource_name}
39
+ end
40
+ EOF
41
+
42
+ if block_given? then instance_eval(&block) end
43
+ end
44
+
45
+ def index(options = {})
46
+ def_source(options, :index)
47
+
48
+ filter = options[:filter] && ".#{options[:filter]}"
49
+ params = options[:params] && "(resource_params_for_index)"
50
+ @controller_class.class_eval <<-EOF
51
+ def resource_index
52
+ resources_set(resource_list(resource_source_for_index)#{filter}#{params})
53
+ end
54
+ EOF
55
+
56
+ @controller_class.class_eval do
57
+ def index
58
+ display resource_index, render_options_for_index
59
+ end
60
+ end
61
+
62
+ inject_render_options(options, :index)
63
+ inject_params(options, :index)
64
+ end
65
+
66
+ def show(options = {})
67
+ def_source(options, :show)
68
+
69
+ @controller_class.class_eval do
70
+ def show
71
+ r = resource_get_for_show(params[:id]) or raise Merb::Controller::NotFound
72
+ display r, render_options_for_show
73
+ end
74
+
75
+ protected
76
+
77
+ def resource_get_for_show(id)
78
+ resource_get(resource_source_for_show, id)
79
+ end
80
+ end
81
+
82
+ inject_render_options(options, :show)
83
+ end
84
+
85
+ def new(options = {})
86
+ def_source(options, :new)
87
+
88
+ @controller_class.class_eval do
89
+ def new
90
+ only_provides :html
91
+ display resource_new, render_options_for_new
92
+ end
93
+
94
+ protected
95
+
96
+ def resource_new
97
+ resource_initialize(resource_source_for_new)
98
+ end
99
+ end
100
+
101
+ inject_render_options(options, :new)
102
+ end
103
+
104
+ def create(options = {})
105
+ def_source(options, :create)
106
+
107
+ @controller_class.class_eval do
108
+ def create
109
+ r = resource_initialize(resource_source_for_create,
110
+ params[self.class::RESOURCE_NAME].merge!(resource_params_for_create))
111
+ if r.save
112
+ resource_created(r)
113
+ else
114
+ message[:error] = "#{self.class::RESOURCE_NAME.humanize} failed to be created"
115
+ render :new, render_options_for_create
116
+ end
117
+ end
118
+
119
+ protected
120
+
121
+ def resource_created(resrc)
122
+ redirect resource_created_route, :message => { :notice => resource_created_message(resrc) }
123
+ end
124
+
125
+ def resource_created_message(resrc)
126
+ "#{self.class::RESOURCE_NAME.singularize} created successfully."
127
+ end
128
+ end
129
+
130
+ inject_route(options, :create)
131
+ inject_params(options, :create)
132
+ inject_render_options(options, :create)
133
+ end
134
+
135
+ def edit(options = {})
136
+ def_source(options, :edit)
137
+
138
+ @controller_class.class_eval do
139
+ def edit
140
+ only_provides :html
141
+ r = resource_get(resource_source_for_edit, params[:id]) or raise ::Merb::Controller::NotFound
142
+ display r, render_options_for_edit
143
+ end
144
+ end
145
+
146
+ inject_render_options(options, :edit)
147
+ end
148
+
149
+ def update(options = {})
150
+ def_source(options, :update)
151
+
152
+ @controller_class.class_eval do
153
+ def update
154
+ r = resource_get(resource_source_for_update, params[:id]) or raise ::Merb::Controller::NotFound
155
+ if r.update(params[self.class::RESOURCE_NAME].merge!(resource_params_for_update))
156
+ resource_updated(r)
157
+ else
158
+ message[:error] = "#{RESOURCE_NAME.humanize} failed to be updated"
159
+ render :edit, render_options_for_update
160
+ end
161
+ end
162
+
163
+ protected
164
+
165
+ def resource_updated(resrc)
166
+ redirect resource_updated_route, :message => {:notice => resource_updated_message(resrc)}
167
+ end
168
+
169
+ def resource_updated_message(resrc)
170
+ "#{self.class::RESOURCE_NAME.humanize} updated successfully."
171
+ end
172
+ end
173
+
174
+ inject_route(options, :update)
175
+ inject_params(options, :update)
176
+ inject_render_options(options, :update)
177
+ end
178
+
179
+ def destroy
180
+ end
181
+
182
+ private
183
+
184
+ def has_parent?(options)
185
+ !! (options[:belongs_to] || @options[:belongs_to])
186
+ end
187
+
188
+ def inject_route(options, method_name)
189
+ to = (options[:to] && options[:to].inspect) || "resource_ivar"
190
+
191
+ if has_parent?(options)
192
+ @controller_class.class_eval <<-EOF
193
+ protected
194
+ def resource_#{method_name}d_route
195
+ resource(resource_parent_get_for_#{method_name}, #{to})
196
+ end
197
+ EOF
198
+ else
199
+ @controller_class.class_eval <<-EOF
200
+ protected
201
+ def resource_#{method_name}d_route
202
+ resource(#{to})
203
+ end
204
+ EOF
205
+ end
206
+ end
207
+
208
+ def inject_render_options(options, method_name)
209
+ if options[:layout]
210
+ @controller_class.class_eval do
211
+ protected
212
+ define_method "render_options_for_#{method_name}" do
213
+ { :layout => options[:layout] }
214
+ end
215
+ end
216
+ else
217
+ @controller_class.class_eval <<-EOF
218
+ protected
219
+ def render_options_for_#{method_name}() {} end
220
+ EOF
221
+ end
222
+ end
223
+
224
+ def inject_params(options, method_name)
225
+ case options[:params]
226
+ when Proc
227
+ @controller_class.class_eval do
228
+ protected
229
+ define_method "resource_params_for_#{method_name}", &options[:params]
230
+ end
231
+ else
232
+ @controller_class.class_eval <<-EOF
233
+ protected
234
+ def resource_params_for_#{method_name}() {} end
235
+ EOF
236
+ end
237
+ end
238
+
239
+ def def_source(options = {}, method_name = nil)
240
+ source = options[:belongs_to] || options[:scope] || @options[:belongs_to] || @options[:scope]
241
+
242
+ case source
243
+ when Symbol
244
+ def_source_with_scope(source, method_name)
245
+ else
246
+ if method_name.nil?
247
+ @controller_class.class_eval <<-EOF
248
+ protected
249
+ def resource_source
250
+ #{@resource_class}
251
+ end
252
+ EOF
253
+ else
254
+ @controller_class.class_eval do
255
+ alias_method "resource_source_for_#{method_name}", :resource_source
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ def def_source_with_scope(source, method_name = nil)
262
+ suffix = method_name && "_for_#{method_name}"
263
+
264
+ s = <<-EOF
265
+ protected
266
+ def resource_source#{suffix}
267
+ #{source}.#{@resource_plural}
268
+ end
269
+
270
+ def resource_parent_get#{suffix}
271
+ #{source}
272
+ end
273
+ EOF
274
+
275
+ @controller_class.class_eval s
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,8 @@
1
+ module Merb
2
+ module Plugins
3
+ module Resourceful
4
+ module Controller
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ if Merb.orm == :datamapper
2
+ module Merb
3
+ module Plugins
4
+ module Resourceful
5
+ module ORMS
6
+ module DataMapperResource
7
+ def resource_list(source)
8
+ resources_set(source.all)
9
+ end
10
+
11
+ def resource_initialize(source, attrs = {})
12
+ resource_set(source.new(attrs))
13
+ end
14
+
15
+ def resource_get(source, id)
16
+ resource_set(source.get(id))
17
+ end
18
+ end
19
+ end
20
+
21
+ module Controller
22
+ include ::Merb::Plugins::Resourceful::ORMS::DataMapperResource
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ require 'english/style_orm'
2
+ require File.join(File.dirname(__FILE__), 'merb_resourceful/builder')
3
+ require File.join(File.dirname(__FILE__), 'merb_resourceful/orms/datamapper_resource')
4
+
5
+ if defined?(Merb)
6
+ module Merb
7
+ module Plugins
8
+ module Resourceful
9
+ module ClassMethods
10
+ def resourceful(options = {}, &block)
11
+ ::Merb::Plugins::Resourceful::Builder.new(self, options).build(&block)
12
+ end
13
+ end # ClassMethods
14
+ end # Resourceful
15
+ end # Plugins
16
+
17
+ class Controller
18
+ extend ::Merb::Plugins::Resourceful::ClassMethods
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ Merb::Config.use do |c|
2
+ c[:default_cookie_domain] = "resourceful.davidleal.com"
3
+ c[:session_id_key] = "key"
4
+ c[:session_secret_key] = "shhhhhh"
5
+ c[:session_expiry] = Merb::Const::WEEK * 4
6
+ c[:log_auto_flush ] = true
7
+ c[:log_level] = :debug
8
+ c[:log_file] = File.join(File.dirname(__FILE__), '..', '..', 'log', 'test.log')
9
+ end
@@ -0,0 +1,71 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+ require File.join(File.dirname(__FILE__), 'resourceful_controller_spec')
3
+
4
+ if Merb.orm == :datamapper
5
+ given "a book exists" do
6
+ Book.all.destroy!
7
+
8
+ @book = (@shelf ? @shelf.books : Book).create(:title => 'Code Complete')
9
+ end
10
+
11
+ given "a shelf exists" do
12
+ Shelf.all.destroy!
13
+
14
+ @shelf = Shelf.create
15
+ end
16
+
17
+ given "2 books exist" do
18
+ @book = (@shelf ? @shelf.books : Book).create(:title => 'Code Complete')
19
+ @shelf2 = Shelf.create
20
+ @book2 = @shelf2.books.create(:title => 'The Practice of Programming')
21
+ end
22
+
23
+ describe "DataMapper resourceful controller" do
24
+ before :all do
25
+ DataMapper.setup(:default, "sqlite3::memory:")
26
+
27
+ class Book
28
+ include DataMapper::Resource
29
+
30
+ property :id, Serial
31
+ property :title, String, :length => 1..255
32
+ property :optional, String
33
+
34
+ def self.zee_filter(*args)
35
+ if param = args.pop
36
+ [Book.new(:title => param)]
37
+ else
38
+ [Book.new(:title => "no params")]
39
+ end
40
+ end
41
+ end
42
+
43
+ class Shelf
44
+ include DataMapper::Resource
45
+
46
+ property :id, Serial
47
+
48
+ has n, :books
49
+ end
50
+ end
51
+
52
+ after :all do
53
+ Object.send(:remove_const, :Book)
54
+ end
55
+
56
+ before do
57
+ Shelf.auto_migrate!
58
+ Book.auto_migrate!
59
+ end
60
+
61
+ it_should_behave_like "resourceful controller"
62
+
63
+ def find_single_book
64
+ Book.first
65
+ end
66
+
67
+ def find_book_in_shelf
68
+ @shelf.books.last
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,441 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
3
+
4
+ describe "resourceful controller", :shared => true do
5
+
6
+ before :all do
7
+ Merb.push_path(:view, File.join(File.dirname(__FILE__), '..', 'views'))
8
+ end
9
+
10
+ before do
11
+ class AbstractBooks < Merb::Controller
12
+ def _template_location(action, type = nil, controller = controller_name)
13
+ controller == "layout" ? "layout.#{action}.#{type}" : "#{action}.#{type}"
14
+ end
15
+ end
16
+
17
+ class ShelvedBooks < AbstractBooks
18
+ def shelf
19
+ @shelf ||= Shelf.all.last
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ after do
26
+ Object.send(:remove_const, :Books)
27
+ end
28
+
29
+ describe "common behavior", :shared => true do
30
+ describe "resource(*, :books)" do
31
+ describe "GET" do
32
+ before(:each) do
33
+ with_resourceful
34
+
35
+ @response = request(resource(*request_for_books))
36
+ end
37
+
38
+ it "responds successfully" do
39
+ @response.should be_successful
40
+ end
41
+
42
+ it "has a list of books" do
43
+ @response.should have_xpath("//ul")
44
+ end
45
+ end
46
+
47
+ describe "GET", :given => "a book exists" do
48
+ before(:each) do
49
+ with_resourceful
50
+
51
+ @response = request(resource(*request_for_books))
52
+ end
53
+
54
+ it "has a list of books" do
55
+ @response.should have_xpath("//ul/li")
56
+ end
57
+ end
58
+
59
+ describe "a successful POST" do
60
+ before(:each) do
61
+ with_resourceful
62
+
63
+ @response = request(resource(*request_for_books), :method => "POST",
64
+ :params => { :book => { :title => "The C Programming Language" }})
65
+ end
66
+
67
+ it "redirects to resource(*, @book)" do
68
+ @response.should redirect_to(resource(*request_for_book),
69
+ :message => {:notice => "book successfully created"})
70
+ end
71
+
72
+ it "creates the resource" do
73
+ find_book.should_not be_nil
74
+ end
75
+ end
76
+
77
+ describe "GET", "with :filter", :given => "a book exists" do
78
+ before(:each) do
79
+ with_resourceful do
80
+ index :filter => :zee_filter
81
+ end
82
+
83
+ @response = request(resource(*request_for_books))
84
+ end
85
+
86
+ it "has a list of filtered books" do
87
+ @response.should have_xpath("//ul/li[. = 'no params']")
88
+ end
89
+ end
90
+
91
+ describe "GET", "with :filter and :params", :given => "a book exists" do
92
+ before(:each) do
93
+ with_resourceful do
94
+ index :filter => :zee_filter, :params => lambda { "whoa" }
95
+ end
96
+
97
+ @response = request(resource(*request_for_books))
98
+ end
99
+
100
+ it "has a list of filtered books" do
101
+ @response.should have_xpath("//ul/li[. = 'whoa']")
102
+ end
103
+ end
104
+
105
+ describe "a successful POST", "with :params" do
106
+ before(:each) do
107
+ with_resourceful do
108
+ create :params => lambda {{ :optional => "is not null!" }}
109
+ end
110
+
111
+ @response = request(resource(*request_for_books), :method => "POST",
112
+ :params => { :book => { :title => "The C Programming Language" }})
113
+ end
114
+
115
+ it "redirects to resource(*, @book)" do
116
+ @response.should redirect_to(resource(*request_for_book),
117
+ :message => {:notice => "book successfully created"})
118
+ end
119
+
120
+ it "adds the value of optional to the book" do
121
+ find_book.optional.should_not be_nil
122
+ end
123
+ end
124
+
125
+ describe "a successful POST", "with :to" do
126
+ before(:each) do
127
+ with_resourceful do
128
+ create :to => :books
129
+ end
130
+
131
+ @response = request(resource(*request_for_books), :method => "POST",
132
+ :params => { :book => { :title => "The C Programming Language" }})
133
+ end
134
+
135
+ it "redirects to resource(*, :books)" do
136
+ @response.should redirect_to(resource(*request_for_books),
137
+ :message => {:notice => "book successfully created"})
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "resource(*, :books, :new)" do
143
+ before(:each) do
144
+ with_resourceful
145
+
146
+ @response = request(resource(*request_for_new_book))
147
+ end
148
+
149
+ it "responds successfully" do
150
+ @response.should be_successful
151
+ end
152
+ end
153
+
154
+ describe "resource(*, @book, :edit)", :given => "a book exists" do
155
+ before(:each) do
156
+ with_resourceful
157
+
158
+ @response = request(resource(*request_for_edit_book))
159
+ end
160
+
161
+ it "responds successfully" do
162
+ @response.should be_successful
163
+ end
164
+
165
+ it "presents the resource being edited" do
166
+ @response.should have_xpath("//*[contains(., 'Edit Code Complete')]")
167
+ end
168
+ end
169
+
170
+ describe "resource(*, @book)", :given => "a book exists" do
171
+ describe "GET" do
172
+ before(:each) do
173
+ with_resourceful
174
+
175
+ @response = request(resource(*request_for_book))
176
+ end
177
+
178
+ it "responds successfully" do
179
+ @response.should be_successful
180
+ end
181
+
182
+ it "shows the right book" do
183
+ @response.should have_xpath("//*[contains(., 'Code Complete')]")
184
+ end
185
+ end
186
+
187
+ describe "PUT" do
188
+ before(:each) do
189
+ with_resourceful
190
+
191
+ @response = request(resource(*request_for_book), :method => "PUT",
192
+ :params => { :book => {:id => @book.id, :title => "Code Complete 2nd Edition."} })
193
+ end
194
+
195
+ it "redirect to the book show action" do
196
+ @response.should redirect_to(resource(*request_for_book))
197
+ end
198
+
199
+ it "updates the book" do
200
+ Book.first.title.should == "Code Complete 2nd Edition."
201
+ end
202
+ end
203
+
204
+ describe "PUT", "with :params" do
205
+ before(:each) do
206
+ with_resourceful do
207
+ update :params => lambda {{ :optional => "is not null!" }}
208
+ end
209
+
210
+ @response = request(resource(*request_for_book), :method => "PUT",
211
+ :params => { :book => {:id => @book.id, :title => "Code Complete 2nd Edition."} })
212
+ end
213
+
214
+ it "redirect to the book show action" do
215
+ @response.should redirect_to(resource(*request_for_book))
216
+ end
217
+
218
+ it "updates the book with the extra param" do
219
+ Book.first.optional.should_not be_nil
220
+ end
221
+ end
222
+
223
+ describe "a successful DELETE" do
224
+ before(:each) do
225
+ pending do
226
+ @response = request(resource(*request_delete), :method => "DELETE")
227
+ end
228
+ end
229
+
230
+ it "should redirect to the index action" do
231
+ pending do
232
+ @response.should redirect_to(resource(*request_for_books))
233
+ end
234
+ end
235
+ end
236
+
237
+ describe "GET" do
238
+ before(:each) do
239
+ with_resourceful do
240
+ show :layout => :another
241
+ end
242
+
243
+ @response = request(resource(*request_for_book))
244
+ end
245
+
246
+ it "has a different layout" do
247
+ @response.should have_xpath("//*[contains(., 'Another Layout')]")
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ describe "action-specific scopes", :shared => true do
254
+ before do
255
+ Books.class_eval do
256
+ def shelf
257
+ @shelf ||= Shelf.all.last
258
+ end
259
+ end
260
+ end
261
+
262
+ describe "resource(*, @book)", :given => "a book exists" do
263
+ describe "GET", "using :scope", :given => "2 books exist" do
264
+ before(:each) do
265
+ with_resourceful do
266
+ show :scope => :shelf
267
+ end
268
+
269
+ @response = request(resource(*request_for_book))
270
+ end
271
+
272
+ it "responds successfully" do
273
+ @response.should be_successful
274
+ end
275
+
276
+ it "shows the right book" do
277
+ @response.should have_xpath("//*[contains(., 'The Practice of Programming')]")
278
+ end
279
+
280
+ def request_for_book
281
+ @book2
282
+ end
283
+ end
284
+
285
+ describe "a successful POST", "using :scope", :given => "a shelf exists" do
286
+ before(:each) do
287
+ with_resourceful do
288
+ create :scope => :shelf
289
+ end
290
+
291
+ @response = request(resource(*request_for_books), :method => "POST",
292
+ :params => { :book => { :title => "The C Programming Language" }})
293
+ end
294
+
295
+ it "redirects to resource(*, @book)" do
296
+ @response.should redirect_to(resource(*find_book),
297
+ :message => {:notice => "book successfully created"})
298
+ end
299
+
300
+ it "creates the book" do
301
+ find_book.title.should == "The C Programming Language"
302
+ end
303
+
304
+ def find_book
305
+ find_book_in_shelf
306
+ end
307
+ end
308
+ end
309
+ end
310
+
311
+ describe "single resource" do
312
+ before :all do
313
+ Merb::Router.prepare do |r|
314
+ identify :id do
315
+ r.resources :books
316
+ end
317
+ end
318
+ end
319
+
320
+ before do
321
+ class Books < AbstractBooks
322
+ end
323
+ end
324
+
325
+ def request_for_books
326
+ :books
327
+ end
328
+
329
+ def request_for_book
330
+ @book || find_book
331
+ end
332
+
333
+ def request_for_new_book
334
+ [:books, :new]
335
+ end
336
+
337
+ def request_for_edit_book
338
+ [@book, :edit]
339
+ end
340
+
341
+ it_should_behave_like "common behavior"
342
+ it_should_behave_like "action-specific scopes"
343
+
344
+ def find_book
345
+ find_single_book
346
+ end
347
+
348
+ def with_resourceful(&block)
349
+ Books.class_eval do
350
+ resourceful &block
351
+ end
352
+ end
353
+ end
354
+
355
+ describe "single resource", "using :scope", :given => "a shelf exists" do
356
+ before :all do
357
+ Merb::Router.prepare do |r|
358
+ identify :id do
359
+ r.resources :books
360
+ end
361
+ end
362
+ end
363
+
364
+ before do
365
+ class Books < ShelvedBooks; end
366
+ end
367
+
368
+ def request_for_books
369
+ :books
370
+ end
371
+
372
+ def request_for_book
373
+ @book || find_book
374
+ end
375
+
376
+ def request_for_new_book
377
+ [:books, :new]
378
+ end
379
+
380
+ def request_for_edit_book
381
+ [@book, :edit]
382
+ end
383
+
384
+ it_should_behave_like "common behavior"
385
+ it_should_behave_like "action-specific scopes"
386
+
387
+ def find_book
388
+ find_book_in_shelf
389
+ end
390
+
391
+ def with_resourceful(&block)
392
+ Books.class_eval do
393
+ resourceful :scope => :shelf, &block
394
+ end
395
+ end
396
+ end
397
+
398
+ describe "nested resource", "through method", :given => "a shelf exists" do
399
+ before :all do
400
+ Merb::Router.prepare do |r|
401
+ identify :id do
402
+ r.resources :shelves do
403
+ r.resources :books
404
+ end
405
+ end
406
+ end
407
+ end
408
+
409
+ before do
410
+ class Books < ShelvedBooks; end
411
+ end
412
+
413
+ def request_for_books
414
+ [@shelf, :books]
415
+ end
416
+
417
+ def request_for_book
418
+ [@shelf, @book || find_book]
419
+ end
420
+
421
+ def request_for_new_book
422
+ [@shelf, :books, :new]
423
+ end
424
+
425
+ def request_for_edit_book
426
+ [@shelf, @book, :edit]
427
+ end
428
+
429
+ it_should_behave_like "common behavior"
430
+
431
+ def find_book
432
+ find_book_in_shelf
433
+ end
434
+
435
+ def with_resourceful(&block)
436
+ Books.class_eval do
437
+ resourceful :belongs_to => :shelf, &block
438
+ end
439
+ end
440
+ end
441
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'merb-core'
4
+
5
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.include(Merb::Test::ViewHelper)
9
+ config.include(Merb::Test::RouteHelper)
10
+ config.include(Merb::Test::ControllerHelper)
11
+ end
12
+
13
+ Merb.start :environment => 'test', :init_file => File.join(File.dirname(__FILE__), 'config', 'init.rb')
14
+
15
+ use_orm (ENV['ORM'] || :datamapper).to_sym
16
+
17
+ require 'merb_resourceful'
18
+ require 'ruby-debug'
19
+
@@ -0,0 +1 @@
1
+ <%= @book.title %>
@@ -0,0 +1 @@
1
+ Edit <%= @book.title %>
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ <% for b in @books %>
3
+ <li><%= b.title %></li>
4
+ <% end -%>
5
+ </ul>
@@ -0,0 +1 @@
1
+ Another Layout
@@ -0,0 +1 @@
1
+ <%= @book.title %>
@@ -0,0 +1 @@
1
+ <%= @book.title %>
@@ -0,0 +1 @@
1
+ <%= @book.title %>
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: david-merb_resourceful
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Leal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-01 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: merb
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "1.0"
23
+ version:
24
+ description: merb_resourceful lends a little magic to common resource actions.
25
+ email: dgleal@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README.textile
32
+ - LICENSE
33
+ - TODO
34
+ files:
35
+ - LICENSE
36
+ - README.textile
37
+ - Rakefile
38
+ - TODO
39
+ - lib/merb_resourceful
40
+ - lib/merb_resourceful/controller.rb
41
+ - lib/merb_resourceful/orms
42
+ - lib/merb_resourceful/orms/datamapper_resource.rb
43
+ - lib/merb_resourceful/builder.rb
44
+ - lib/merb_resourceful.rb
45
+ - spec/merb_resourceful
46
+ - spec/merb_resourceful/datamapper_resourceful_controller_spec.rb
47
+ - spec/merb_resourceful/resourceful_controller_spec.rb
48
+ - spec/merb_resourceful/log
49
+ - spec/merb_resourceful/log/merb_test.log
50
+ - spec/merb_resourceful/log/merb.main.pid
51
+ - spec/spec_helper.rb
52
+ - spec/views
53
+ - spec/views/create.html.erb
54
+ - spec/views/edit.html.erb
55
+ - spec/views/new.html.erb
56
+ - spec/views/layout.another.html.erb
57
+ - spec/views/update.html.erb
58
+ - spec/views/show.html.erb
59
+ - spec/views/index.html.erb
60
+ - spec/config
61
+ - spec/config/init.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/david/merb_resourceful
64
+ post_install_message:
65
+ rdoc_options: []
66
+
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ requirements: []
82
+
83
+ rubyforge_project: merb
84
+ rubygems_version: 1.2.0
85
+ signing_key:
86
+ specification_version: 2
87
+ summary: merb_resourceful lends a little magic to common resource actions.
88
+ test_files: []
89
+