rest-sinatra 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ Copyright (c) 2009, Sunlight Foundation
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+ * Neither the name of Sunlight Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ ## About
2
+
3
+ With rest-sinatra, success is all but guaranteed in writing RESTful Web Services. (Provided that you are using a [Sinatra](http://sinatrarb.com) + [MongoMapper](http://github.com/jnunemaker/mongomapper) stack.)
4
+
5
+ ## Installation
6
+
7
+ It might not be a bad idea to make sure you are running the latest RubyGems:
8
+
9
+ sudo gem update --system
10
+
11
+ I recommend a user-level install (no sudo needed):
12
+
13
+ gem install djsun-rest-sinatra
14
+
15
+ Note: in general, beware of `sudo gem install <project_name>` -- it gives elevated privileges. Do you trust `<project name>`? Better to be safe and use a local install to `~/.gem`.
16
+
17
+ ## Usage
18
+
19
+ For a basic example of what this looks like when integrated into a real-world Sinatra app, see [sources.rb](http://github.com/sunlightlabs/datacatalog-api/blob/master/controllers/sources.rb). For an example of nested resources, see [users.rb](http://github.com/sunlightlabs/datacatalog-api/blob/master/controllers/users.rb) and [users_keys.rb](http://github.com/sunlightlabs/datacatalog-api/blob/master/controllers/users_keys.rb).
20
+
21
+ ## History
22
+
23
+ This code was extracted from the [National Data Catalog](http://groups.google.com/group/datacatalog), a project of the [Sunlight Labs](http://sunlightlabs.com).
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rest-sinatra"
8
+ gem.summary = %Q{Easily write RESTful actions with Sinatra and MongoMapper}
9
+ gem.description = %Q{Provides a DSL for making RESTful Sinatra actions with MongoMapper models.}
10
+ gem.email = "djames@sunlightfoundation.com"
11
+ gem.homepage = "http://github.com/djsun/rest-sinatra"
12
+ gem.authors = ["David James"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :spec => :check_dependencies
33
+
34
+ task :default => :spec
35
+
36
+ require 'rake/rdoctask'
37
+ Rake::RDocTask.new do |rdoc|
38
+ if File.exist?('VERSION')
39
+ version = File.read('VERSION')
40
+ else
41
+ version = ""
42
+ end
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "rest-sinatra #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
@@ -0,0 +1,287 @@
1
+ module RestSinatra
2
+
3
+ def self.included(includer)
4
+ includer.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ attr_reader :config
10
+
11
+ def resource(name, &block)
12
+ _resource(name, :regular, &block)
13
+ end
14
+
15
+ def nestable_resource(name, &block)
16
+ _resource(name, :nestable, &block)
17
+ end
18
+
19
+ protected
20
+
21
+ def _resource(name, resource_type, &block)
22
+ config = evaluate_block(name, resource_type, &block)
23
+ validate(config)
24
+ build_resource(config)
25
+ config
26
+ end
27
+
28
+ def evaluate_block(name, resource_type, &block)
29
+ scope = Object.new
30
+ scope.extend(ResourceMethods)
31
+ scope.instance_eval do
32
+ @c = {
33
+ :name => name,
34
+ :resource_type => resource_type,
35
+ :model => nil,
36
+ :read_only => [],
37
+ :permission => nil,
38
+ :callbacks => {},
39
+ :nested_resources => []
40
+ }
41
+ end
42
+ scope.instance_eval(&block)
43
+ scope.instance_variable_get("@c")
44
+ end
45
+
46
+ def validate(c)
47
+ raise "name required" unless c[:name]
48
+ raise "model required" unless c[:model]
49
+ c[:nested_resources].each do |resource|
50
+ unless resource[:association]
51
+ raise "association required for #{resource[:class]}"
52
+ end
53
+ end
54
+ end
55
+
56
+ def build_resource(config)
57
+ case config[:resource_type]
58
+ when :regular
59
+ build_parent_resource(config)
60
+ build_nested_resources(config)
61
+ when :nestable
62
+ save_nestable_config(config)
63
+ else
64
+ raise "Unexpected resource_type"
65
+ end
66
+ end
67
+
68
+ def build_parent_resource(config)
69
+ callbacks = config[:callbacks]
70
+ model = config[:model]
71
+ name = config[:name]
72
+ read_only = config[:read_only]
73
+
74
+ get '/?' do
75
+ require_at_least(:basic)
76
+ validate_before_find_all(params, model)
77
+ @documents = find_with_filters(params, model)
78
+ @documents.to_json
79
+ end
80
+
81
+ get '/:id/?' do |id|
82
+ require_at_least(:basic)
83
+ id = params.delete("id")
84
+ validate_before_find_one(params, model)
85
+ @document = find_document!(model, id)
86
+ @document.to_json
87
+ end
88
+
89
+ post '/?' do
90
+ require_at_least(:curator)
91
+ validate_before_create(params, model, read_only)
92
+ callback(callbacks[:before_save])
93
+ callback(callbacks[:before_create])
94
+ @document = model.new(params)
95
+ unless @document.valid?
96
+ error 400, { "errors" => @document.errors.errors }.to_json
97
+ end
98
+ @document.save
99
+ callback(callbacks[:after_create])
100
+ callback(callbacks[:after_save])
101
+ response.status = 201
102
+ response.headers['Location'] = full_uri "/#{name}/#{@document.id}"
103
+ @document.to_json
104
+ end
105
+
106
+ put '/:id/?' do
107
+ require_at_least(:curator)
108
+ id = params.delete("id")
109
+ @document = find_document!(model, id)
110
+ validate_before_update(params, model, read_only)
111
+ callback(callbacks[:before_save])
112
+ callback(callbacks[:before_update])
113
+ @document = model.update(id, params)
114
+ unless @document.valid?
115
+ error 400, { "errors" => @document.errors.errors }.to_json
116
+ end
117
+ callback(callbacks[:after_update])
118
+ callback(callbacks[:after_save])
119
+ @document.to_json
120
+ end
121
+
122
+ delete '/:id/?' do
123
+ require_at_least(:curator)
124
+ id = params.delete("id")
125
+ @document = find_document!(model, id)
126
+ callback(callbacks[:before_destroy])
127
+ @document.destroy
128
+ callback(callbacks[:after_destroy])
129
+ { "id" => id }.to_json
130
+ end
131
+
132
+ helpers do
133
+ def find_document!(model, id)
134
+ document = model.find_by_id(id)
135
+ error 404, [].to_json unless document
136
+ document
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ def build_nested_resources(parent_config)
143
+ parent_config[:nested_resources].each do |resource|
144
+ nested_res_class = resource[:class]
145
+ assoc = resource[:association]
146
+ nested_config = restore_nestable_config(nested_res_class)
147
+ build_nested_resource(nested_res_class, assoc, parent_config, nested_config)
148
+ end
149
+ end
150
+
151
+ # klass : nested resource class
152
+ # association : a method on the parent model that will return child models
153
+ def build_nested_resource(klass, association, parent_config, child_config)
154
+ callbacks = child_config[:callbacks]
155
+ child_model = child_config[:model]
156
+ child_name = child_config[:name]
157
+ permission = child_config[:permission]
158
+ read_only = child_config[:read_only]
159
+
160
+ parent_model = parent_config[:model]
161
+ parent_name = parent_config[:name]
162
+
163
+ get "/:parent_id/#{child_name}/?" do
164
+ parent_id = params.delete("parent_id")
165
+ permission_check(:basic, permission, parent_id)
166
+ @parent_document = find_parent!(parent_model, parent_id)
167
+ all_child_documents = @parent_document.send(association)
168
+ validate_before_find_all(params, child_model) # ?
169
+ @child_documents = nested_find_with_filters(all_child_documents, params, parent_model)
170
+ @child_documents.to_json
171
+ end
172
+
173
+ get "/:parent_id/#{child_name}/:child_id/?" do
174
+ parent_id = params.delete("parent_id")
175
+ permission_check(:basic, permission, parent_id)
176
+ child_id = params.delete("child_id")
177
+ validate_before_find_one(params, child_model) # ?
178
+ @parent_document, @child_document = find_documents!(parent_model, parent_id, association, child_id)
179
+ @child_document.to_json
180
+ end
181
+
182
+ post "/:parent_id/#{child_name}/?" do
183
+ parent_id = params.delete("parent_id")
184
+ permission_check(:curator, permission, parent_id)
185
+ @parent_document = find_parent!(parent_model, parent_id)
186
+ validate_before_create(params, child_model, read_only)
187
+ callback(callbacks[:before_save])
188
+ callback(callbacks[:before_create])
189
+ @child_document = child_model.new(params)
190
+ @parent_document.send(association) << @child_document
191
+ error 500, [].to_json unless @parent_document.save
192
+ callback(callbacks[:after_create])
193
+ callback(callbacks[:after_save])
194
+ response.status = 201
195
+ response.headers['Location'] = full_uri(
196
+ "/#{parent_name}/#{parent_id}/#{child_name}/#{@child_document.id}"
197
+ )
198
+ @child_document.to_json
199
+ end
200
+
201
+ put "/:parent_id/#{child_name}/:child_id/?" do
202
+ parent_id = params.delete("parent_id")
203
+ permission_check(:curator, permission, parent_id)
204
+ child_id = params.delete("child_id")
205
+ @parent_document, @child_document = find_documents!(parent_model, parent_id, association, child_id)
206
+ validate_before_update(params, child_model, read_only)
207
+ callback(callbacks[:before_save])
208
+ callback(callbacks[:before_update])
209
+ @child_document.attributes = params
210
+ child_index = @parent_document.send(association).index(@child_document)
211
+ @parent_document.send(association)[child_index] = @child_document
212
+ error 500, [].to_json unless @parent_document.save
213
+ callback(callbacks[:after_update])
214
+ callback(callbacks[:after_save])
215
+ @child_document.to_json
216
+ end
217
+
218
+ delete "/:parent_id/#{child_name}/:child_id/?" do
219
+ parent_id = params.delete("parent_id")
220
+ permission_check(:curator, permission, parent_id)
221
+ child_id = params.delete("child_id")
222
+ @parent_document, @child_document = find_documents!(parent_model, parent_id, association, child_id)
223
+ callback(callbacks[:before_destroy])
224
+ @parent_document.send(association).delete(@child_document)
225
+ callback(callbacks[:after_destroy])
226
+ error 500, [].to_json unless @parent_document.save
227
+ { "id" => child_id }.to_json
228
+ end
229
+
230
+ helpers do
231
+ def find_parent!(parent_model, parent_id)
232
+ parent_document = parent_model.find_by_id(parent_id)
233
+ error 404, [].to_json unless parent_document
234
+ parent_document
235
+ end
236
+
237
+ def find_child!(parent_document, association, child_id)
238
+ child_document = parent_document.send(association).find { |x| x.id == child_id }
239
+ error 404, [].to_json unless child_document
240
+ child_document
241
+ end
242
+
243
+ def find_documents!(parent_model, parent_id, association, child_id)
244
+ parent_document = find_parent!(parent_model, parent_id)
245
+ child_document = find_child!(parent_document, association, child_id)
246
+ [parent_document, child_document]
247
+ end
248
+ end
249
+ end
250
+
251
+ def save_nestable_config(config)
252
+ @nestable_resource_config = config
253
+ end
254
+
255
+ def restore_nestable_config(klass)
256
+ klass.instance_variable_get("@nestable_resource_config")
257
+ end
258
+
259
+ end
260
+
261
+ module ResourceMethods
262
+
263
+ def model(model)
264
+ raise "model already declared" if @c[:model]
265
+ @c[:model] = model
266
+ end
267
+
268
+ def read_only(attribute)
269
+ @c[:read_only] << attribute
270
+ end
271
+
272
+ def permission(level)
273
+ raise "permission already declared" if @c[:permission]
274
+ @c[:permission] = level
275
+ end
276
+
277
+ def callback(name, &block)
278
+ @c[:callbacks][name] = block
279
+ end
280
+
281
+ def nested_resource(klass, options)
282
+ @c[:nested_resources] << options.merge({:class => klass})
283
+ end
284
+
285
+ end
286
+
287
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rest-sinatra}
8
+ s.version = "0.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["David James"]
12
+ s.date = %q{2009-10-02}
13
+ s.description = %q{Provides a DSL for making RESTful Sinatra actions with MongoMapper models.}
14
+ s.email = %q{djames@sunlightfoundation.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/rest-sinatra.rb",
27
+ "rest-sinatra.gemspec",
28
+ "spec/spec_helper.rb",
29
+ "spec/unit/helpers/comments.rb",
30
+ "spec/unit/helpers/posts.rb",
31
+ "spec/unit/helpers/sinatra_stubs.rb",
32
+ "spec/unit/helpers/sources.rb",
33
+ "spec/unit/posts_and_comments_spec.rb",
34
+ "spec/unit/sources_spec.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/djsun/rest-sinatra}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.5}
40
+ s.summary = %q{Easily write RESTful actions with Sinatra and MongoMapper}
41
+ s.test_files = [
42
+ "spec/spec_helper.rb",
43
+ "spec/unit/helpers/comments.rb",
44
+ "spec/unit/helpers/posts.rb",
45
+ "spec/unit/helpers/sinatra_stubs.rb",
46
+ "spec/unit/helpers/sources.rb",
47
+ "spec/unit/posts_and_comments_spec.rb",
48
+ "spec/unit/sources_spec.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
56
+ s.add_development_dependency(%q<rspec>, [">= 0"])
57
+ else
58
+ s.add_dependency(%q<rspec>, [">= 0"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<rspec>, [">= 0"])
62
+ end
63
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rest-sinatra'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/sinatra_stubs')
2
+
3
+ class Comment ; end
4
+
5
+ class Comments
6
+ include RestSinatra
7
+ include SinatraStubs
8
+
9
+ @r = nestable_resource "comments" do
10
+ model Comment
11
+
12
+ read_only :created_at
13
+
14
+ callback :before_save do
15
+ "before saving comments"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/sinatra_stubs')
2
+ require File.expand_path(File.dirname(__FILE__) + '/comments')
3
+
4
+ class Post ; end
5
+
6
+ class Posts
7
+ include RestSinatra
8
+ include SinatraStubs
9
+
10
+ @r = resource "posts" do
11
+ model Post
12
+
13
+ read_only :created_at
14
+ read_only :updated_at
15
+
16
+ nested_resource Comments, :association => :comments
17
+
18
+ callback :before_save do
19
+ "before saving posts"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ module SinatraStubs
2
+
3
+ def self.included(includer)
4
+ includer.class_eval do
5
+ @actions = {
6
+ :get => [],
7
+ :post => [],
8
+ :put => [],
9
+ :delete => []
10
+ }
11
+ end
12
+ includer.extend(ClassMethods)
13
+ end
14
+
15
+ module ClassMethods
16
+ attr_reader :actions
17
+
18
+ def get(route)
19
+ @actions[:get] << route
20
+ end
21
+
22
+ def post(route)
23
+ @actions[:post] << route
24
+ end
25
+
26
+ def put(route)
27
+ @actions[:put] << route
28
+ end
29
+
30
+ def delete(route)
31
+ @actions[:delete] << route
32
+ end
33
+
34
+ def helpers()
35
+ # ...
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/sinatra_stubs')
2
+
3
+ class Source ; end
4
+
5
+ class Sources
6
+ include RestSinatra
7
+ include SinatraStubs
8
+
9
+ @r = resource "sources" do
10
+ model Source
11
+ end
12
+ end
@@ -0,0 +1,148 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+ require File.expand_path(File.dirname(__FILE__) + '/helpers/posts.rb')
3
+ require File.expand_path(File.dirname(__FILE__) + '/helpers/comments.rb')
4
+
5
+ describe "Posts" do
6
+
7
+ before do
8
+ @config = Posts.instance_variable_get("@r")
9
+ end
10
+
11
+ describe "DSL methods" do
12
+ it "resource should be 'posts'" do
13
+ @config[:name].should == "posts"
14
+ end
15
+
16
+ it "model should be Post" do
17
+ @config[:model].should == Post
18
+ end
19
+
20
+ it "read_only should be correct" do
21
+ @config[:read_only].should == [:created_at, :updated_at]
22
+ end
23
+
24
+ it "callback :before_create should be correct" do
25
+ @config[:callbacks][:before_save].call.should == "before saving posts"
26
+ end
27
+
28
+ it "callback :before_update should return nil" do
29
+ @config[:callbacks][:before_update].should == nil
30
+ end
31
+ end
32
+
33
+ describe "actions" do
34
+ before do
35
+ @actions = Posts.actions
36
+ end
37
+
38
+ describe "get" do
39
+ before do
40
+ @acts = @actions[:get]
41
+ end
42
+
43
+ it "get /?" do
44
+ @acts.should include("/?")
45
+ end
46
+
47
+ it "get /:id/?" do
48
+ @acts.should include("/:id/?")
49
+ end
50
+
51
+ it "get /:parent_id/comments/?" do
52
+ @acts.should include("/:parent_id/comments/?")
53
+ end
54
+
55
+ it "get /:parent_id/comments/:child_id/?" do
56
+ @acts.should include("/:parent_id/comments/:child_id/?")
57
+ end
58
+
59
+ it "exactly 4 actions" do
60
+ @acts.length.should == 4
61
+ end
62
+ end
63
+
64
+ describe "post" do
65
+ before do
66
+ @acts = @actions[:post]
67
+ end
68
+
69
+ it "post /?" do
70
+ @acts.should include("/?")
71
+ end
72
+
73
+ it "post /:parent_id/comments/?" do
74
+ @acts.should include("/:parent_id/comments/?")
75
+ end
76
+
77
+ it "exactly 2 actions" do
78
+ @acts.length.should == 2
79
+ end
80
+ end
81
+
82
+ describe "put" do
83
+ before do
84
+ @acts = @actions[:put]
85
+ end
86
+
87
+ it "put /:id/?" do
88
+ @acts.should include("/:id/?")
89
+ end
90
+
91
+ it "put /:parent_id/comments/:child_id/?" do
92
+ @acts.should include("/:parent_id/comments/:child_id/?")
93
+ end
94
+
95
+ it "exactly 2 actions" do
96
+ @acts.length.should == 2
97
+ end
98
+ end
99
+
100
+ describe "delete" do
101
+ before do
102
+ @acts = @actions[:delete]
103
+ end
104
+
105
+ it "delete /:id/?" do
106
+ @acts.should include("/:id/?")
107
+ end
108
+
109
+ it "delete /:parent_id/comments/:child_id/?" do
110
+ @acts.should include("/:parent_id/comments/:child_id/?")
111
+ end
112
+
113
+ it "exactly 2 actions" do
114
+ @acts.length.should == 2
115
+ end
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ describe "Comments" do
122
+
123
+ before do
124
+ @config = Comments.instance_variable_get("@r")
125
+ end
126
+
127
+ describe "DSL methods" do
128
+ it "resource should be 'comments'" do
129
+ @config[:name].should == "comments"
130
+ end
131
+
132
+ it "model should be Comment" do
133
+ @config[:model].should == Comment
134
+ end
135
+
136
+ it "read_only should be correct" do
137
+ @config[:read_only].should == [:created_at]
138
+ end
139
+
140
+ it "callback :before_create should be correct" do
141
+ @config[:callbacks][:before_save].call.should == "before saving comments"
142
+ end
143
+
144
+ it "callback :before_update should return nil" do
145
+ @config[:callbacks][:before_update].should == nil
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/helpers/sources')
3
+
4
+ describe "Sources" do
5
+
6
+ describe "DSL methods" do
7
+ before do
8
+ @config = Sources.instance_variable_get("@r")
9
+ end
10
+
11
+ it "name should be 'sources'" do
12
+ @config[:name].should == "sources"
13
+ end
14
+
15
+ it "model should be Source" do
16
+ @config[:model].should == Source
17
+ end
18
+
19
+ it "read_only should be empty" do
20
+ @config[:read_only].should == []
21
+ end
22
+
23
+ it "callbacks should be empty" do
24
+ @config[:callbacks].should == {}
25
+ end
26
+ end
27
+
28
+ describe "actions" do
29
+ before do
30
+ @actions = Sources.actions
31
+ end
32
+
33
+ describe "get" do
34
+ before do
35
+ @acts = @actions[:get]
36
+ end
37
+
38
+ it "get /?" do
39
+ @acts.should include("/?")
40
+ end
41
+
42
+ it "get /:id/?" do
43
+ @acts.should include("/:id/?")
44
+ end
45
+
46
+ it "exactly 2 actions" do
47
+ @acts.length.should == 2
48
+ end
49
+ end
50
+
51
+ describe "post" do
52
+ before do
53
+ @acts = @actions[:post]
54
+ end
55
+
56
+ it "post /?" do
57
+ @acts.should include("/?")
58
+ end
59
+
60
+ it "exactly 1 action" do
61
+ @acts.length.should == 1
62
+ end
63
+ end
64
+
65
+ describe "put" do
66
+ before do
67
+ @acts = @actions[:put]
68
+ end
69
+
70
+ it "put /:id/?" do
71
+ @acts.should include("/:id/?")
72
+ end
73
+
74
+ it "exactly 1 action" do
75
+ @acts.length.should == 1
76
+ end
77
+ end
78
+
79
+ describe "delete" do
80
+ before do
81
+ @acts = @actions[:delete]
82
+ end
83
+
84
+ it "delete /:id/?" do
85
+ @acts.should include("/:id/?")
86
+ end
87
+
88
+ it "exactly 1 action" do
89
+ @acts.length.should == 1
90
+ end
91
+ end
92
+ end
93
+
94
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rest-sinatra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - David James
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-02 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Provides a DSL for making RESTful Sinatra actions with MongoMapper models.
26
+ email: djames@sunlightfoundation.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.md
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.md
39
+ - Rakefile
40
+ - VERSION
41
+ - lib/rest-sinatra.rb
42
+ - rest-sinatra.gemspec
43
+ - spec/spec_helper.rb
44
+ - spec/unit/helpers/comments.rb
45
+ - spec/unit/helpers/posts.rb
46
+ - spec/unit/helpers/sinatra_stubs.rb
47
+ - spec/unit/helpers/sources.rb
48
+ - spec/unit/posts_and_comments_spec.rb
49
+ - spec/unit/sources_spec.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/djsun/rest-sinatra
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.5
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Easily write RESTful actions with Sinatra and MongoMapper
78
+ test_files:
79
+ - spec/spec_helper.rb
80
+ - spec/unit/helpers/comments.rb
81
+ - spec/unit/helpers/posts.rb
82
+ - spec/unit/helpers/sinatra_stubs.rb
83
+ - spec/unit/helpers/sources.rb
84
+ - spec/unit/posts_and_comments_spec.rb
85
+ - spec/unit/sources_spec.rb