coast 0.9.1 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.md +186 -0
- data/lib/coast.rb +25 -3
- data/lib/coast/version.rb +3 -0
- data/test/test_coast.rb +248 -0
- metadata +99 -10
- data/spec/coast_spec.rb +0 -245
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Nathan Hopkins
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
# Coast
|
2
|
+
|
3
|
+
## Providing resourceful behavior for [Rails controllers](http://guides.rubyonrails.org/action_controller_overview.html)
|
4
|
+
|
5
|
+
![Coast GEM](http://hopsoft.github.com/coast/images/coast.png)
|
6
|
+
|
7
|
+
### ...if only the REST of life were this easy
|
8
|
+
|
9
|
+
Simply include the Coast module in your controller and get these actions for free.
|
10
|
+
|
11
|
+
`new` `edit` `index` `show` `create` `update` `destroy`
|
12
|
+
|
13
|
+
But wait... there's more.
|
14
|
+
|
15
|
+
* **Lightweight** - about 200 lines of code... *you can grok it in 5 minutes by skimming lib/coast.rb*
|
16
|
+
* **Unobtrusive** - no monkey patches
|
17
|
+
* **Flexible** - support for html, xml, and json formats
|
18
|
+
* **Familiar** - simple DSL for hooking into action callbacks
|
19
|
+
* **Secure** - implicit authorization with your favorite libs... *such as [CanCan](https://github.com/ryanb/cancan)*
|
20
|
+
|
21
|
+
### Works best when you stick to [Rails conventions](http://guides.rubyonrails.org/getting_started.html)
|
22
|
+
|
23
|
+
## Quick-start for the lazy
|
24
|
+
|
25
|
+
Assume you have a simple app structure for beach bums.
|
26
|
+
|
27
|
+
```bash
|
28
|
+
app/controllers/beach_bums_controller.rb
|
29
|
+
app/models/beach_bum.rb
|
30
|
+
```
|
31
|
+
|
32
|
+
Install the GEM.
|
33
|
+
|
34
|
+
```bash
|
35
|
+
$gem install coast
|
36
|
+
```
|
37
|
+
|
38
|
+
Tweak some files.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# config/routes.rb
|
42
|
+
Beach::Application.routes.draw do
|
43
|
+
resources :beach_bums
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# app/controllers/beach_bums_controller.rb
|
49
|
+
class BeachBumsController < ApplicationController
|
50
|
+
include Coast
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Congratulations... you now have a RESTful API for **beach bums**.
|
55
|
+
|
56
|
+
## Callbacks
|
57
|
+
|
58
|
+
Coast uses a [Sinatra](http://www.sinatrarb.com/) like DSL to provide hooks into the action lifecycle.
|
59
|
+
|
60
|
+
The following hooks are supported for each action.
|
61
|
+
|
62
|
+
* `before` *- before any other action logic is performed... just like a [Rails before_filter](http://guides.rubyonrails.org/action_controller_overview.html#filters)*
|
63
|
+
* `respond_to` *- after authorization and db work but before rendering or redirecting*
|
64
|
+
* `after` *- after all other action logic is performed... just like a [Rails after_filter](http://guides.rubyonrails.org/action_controller_overview.html#filters)*
|
65
|
+
|
66
|
+
### How to use the callbacks
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# app/controllers/beach_bums_controller.rb
|
70
|
+
class BeachBumsController < ApplicationController
|
71
|
+
include Coast
|
72
|
+
|
73
|
+
before :show do
|
74
|
+
# take control and load a 'beach_bum' instead of letting Coast do it for us
|
75
|
+
@resourceful_item = BeachBum.find(params[:id])
|
76
|
+
|
77
|
+
# Coast will implicitly create an @beach_bum variable that references @resourceful_item
|
78
|
+
# cool eh?
|
79
|
+
end
|
80
|
+
|
81
|
+
respond_to :show do
|
82
|
+
# take control of rendering or redirecting instead of letting Coast do it for us
|
83
|
+
render :text => "Out surfing."
|
84
|
+
end
|
85
|
+
|
86
|
+
after :show do
|
87
|
+
# do some last minute housekeeping after every thing else is done
|
88
|
+
Rails.logger.info "Hey brah... we just completed the show action."
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Authorization
|
95
|
+
|
96
|
+
Coast implicitly calls an authorize method prior to executing any action logic.
|
97
|
+
|
98
|
+
You have complete control over this method. Here's an example.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
# app/controllers/beach_bums_controller.rb
|
102
|
+
class BeachBumsController < ApplicationController
|
103
|
+
include Coast
|
104
|
+
set_authorize_method :authorize
|
105
|
+
|
106
|
+
def authorize(action, data, request)
|
107
|
+
# restrict all RESTful actions
|
108
|
+
raise "Unauthorized"
|
109
|
+
end
|
110
|
+
|
111
|
+
rescue_from Exception do |ex|
|
112
|
+
render :text => "Not Allowed", :status => 401
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
Note the authorize method signature. The first arg is the **action** being performed. The second arg is the **record(s)** being operated on. The last arg is the **request** object.
|
118
|
+
|
119
|
+
While originally written to support [CanCan](https://github.com/ryanb/cancan), its pretty simple to take control and manage authorization yourself.
|
120
|
+
|
121
|
+
## Advanced Usage
|
122
|
+
|
123
|
+
Coast comes with few tricks up its sleeve.
|
124
|
+
|
125
|
+
If your model and controller names deviate from Rails conventions, you can explicitly set the model like so.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# app/controllers/beach_bums_controller.rb
|
129
|
+
class BeachBumsController < ApplicationController
|
130
|
+
include Coast
|
131
|
+
set_resourceful_model SurferDude
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
You can conditionally prevent mutating behavior on the server by setting an instance variable like so. *It's a little arcane, but that's on purpose.*
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# app/controllers/beach_bums_controller.rb
|
139
|
+
class BeachBumsController < ApplicationController
|
140
|
+
include Coast
|
141
|
+
|
142
|
+
before :create do
|
143
|
+
# prevent the user from actually creating a record
|
144
|
+
@skip_db_create = true
|
145
|
+
end
|
146
|
+
|
147
|
+
before :update do
|
148
|
+
# prevent the user from actually saving a record
|
149
|
+
@skip_db_update = true
|
150
|
+
end
|
151
|
+
|
152
|
+
before :destroy do
|
153
|
+
# prevent the user from actually destroying a record
|
154
|
+
@skip_db_destroy = true
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
## Testing
|
161
|
+
|
162
|
+
There are some interesting additions to MiniTest::Mock since I mock some of Rails to make testing fast & fun.
|
163
|
+
|
164
|
+
Poke around the test code and let me know what you think.
|
165
|
+
|
166
|
+
How to run the tests.
|
167
|
+
|
168
|
+
```bash
|
169
|
+
$rvm 1.9.3
|
170
|
+
$gem install bundler
|
171
|
+
$bundle
|
172
|
+
$rake test
|
173
|
+
```
|
174
|
+
|
175
|
+
Ahh... passing tests.
|
176
|
+
|
177
|
+
## Contributing
|
178
|
+
|
179
|
+
I'm looking for hand-outs, so please fork and submit pull requests. Bug fixes, features, whatever...
|
180
|
+
|
181
|
+
## Nods
|
182
|
+
|
183
|
+
[I'm tired of writing RESTful boilerplate (& scaffold is overkill), so I'm stoked natehop released the Coast gem.](https://twitter.com/tehviking/status/189739333857710080)
|
184
|
+
|
185
|
+
-- Brandon Hayes, April 10, 2012
|
186
|
+
|
data/lib/coast.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.require :default
|
4
|
+
require File.join(File.dirname(__FILE__), "coast", "version")
|
5
|
+
|
1
6
|
# Makes any controller resourceful by providing the following actions:
|
2
7
|
# * new
|
3
8
|
# * edit
|
@@ -32,6 +37,14 @@
|
|
32
37
|
module Coast
|
33
38
|
|
34
39
|
module ClassMethods
|
40
|
+
def set_localized(arg)
|
41
|
+
@set_localized = arg
|
42
|
+
end
|
43
|
+
alias :localized= :set_localized
|
44
|
+
|
45
|
+
def localized?
|
46
|
+
@set_localized
|
47
|
+
end
|
35
48
|
|
36
49
|
def set_authorize_method(arg)
|
37
50
|
@authorize_method = arg
|
@@ -155,7 +168,7 @@ module Coast
|
|
155
168
|
unless performed?
|
156
169
|
respond_to do |format|
|
157
170
|
if success
|
158
|
-
|
171
|
+
write_to_flash :notice, "#{resourceful_model.name} was successfully created."
|
159
172
|
format.html { redirect_to(@resourceful_item) }
|
160
173
|
format_json_and_xml(format, @resourceful_item, :status => :created, :location => @resourceful_item)
|
161
174
|
else
|
@@ -177,7 +190,7 @@ module Coast
|
|
177
190
|
unless performed?
|
178
191
|
respond_to do |format|
|
179
192
|
if success
|
180
|
-
|
193
|
+
write_to_flash :notice, "#{resourceful_model.name} was successfully updated."
|
181
194
|
format.html { redirect_to(@resourceful_item) }
|
182
195
|
format_json_and_xml(format, @resourceful_item)
|
183
196
|
else
|
@@ -197,7 +210,7 @@ module Coast
|
|
197
210
|
@resourceful_item.destroy unless @skip_db_destroy
|
198
211
|
invoke_callback(:respond_to_destroy)
|
199
212
|
unless performed?
|
200
|
-
|
213
|
+
write_to_flash(:notice, "#{resourceful_model.name} was successfully destroyed.") if @resourceful_item.destroyed?
|
201
214
|
respond_to do |format|
|
202
215
|
format.html { redirect_to root_url }
|
203
216
|
format_json_and_xml(format, @resourceful_item)
|
@@ -233,6 +246,15 @@ module Coast
|
|
233
246
|
|
234
247
|
private
|
235
248
|
|
249
|
+
def write_to_flash(key, message)
|
250
|
+
if self.class.localized?
|
251
|
+
message_key = message.downcase.strip.gsub(/\W+$/, "").gsub(/\W/, "_")
|
252
|
+
flash[key] = I18n.translate(message_key)
|
253
|
+
else
|
254
|
+
flash[key] = message
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
236
258
|
def invoke_callback(name)
|
237
259
|
send(name) if respond_to?(name)
|
238
260
|
end
|
data/test/test_coast.rb
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.require(:default, :test)
|
4
|
+
require "active_support/all"
|
5
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "coast")
|
6
|
+
|
7
|
+
class TestCoast < MicroTest::Test
|
8
|
+
|
9
|
+
RESTFUL_METHODS = %w(index show new edit create update destroy)
|
10
|
+
ITEM_METHODS = %w(show new edit create update destroy)
|
11
|
+
UI_METHODS = %w(new edit)
|
12
|
+
READ_METHODS = %w(index show)
|
13
|
+
MUTATE_METHODS = %w(create update destroy)
|
14
|
+
|
15
|
+
# Creates a mock model instance [ActiveRecord::Base].
|
16
|
+
def self.mock_model
|
17
|
+
mock = MicroMock.make.new
|
18
|
+
mock.stub(:destroy) { @destroyed = true }
|
19
|
+
mock.stub(:destroyed?) { @destroyed }
|
20
|
+
mock.stub(:update_attributes) { |*args| @attributes_updated = true }
|
21
|
+
mock.stub(:save) { |*args| @saved = true }
|
22
|
+
mock.stub(:table_name) { "mocks" }
|
23
|
+
mock.class.stub(:find) { |*args| TestCoast.mock_model }
|
24
|
+
mock.class.stub(:all) { [1..5].map { TestCoast.mock_model } }
|
25
|
+
mock
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a mock request instance [ActionDispatch::Request].
|
29
|
+
def self.mock_request
|
30
|
+
mock = MicroMock.make.new
|
31
|
+
mock.stub(:request) { @request ||= MicroMock.make.new }
|
32
|
+
mock.stub(:params) { @params ||= {} }
|
33
|
+
mock.stub(:flash) { @flash ||= {} }
|
34
|
+
mock
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a mock controller instance [ActionController::Base].
|
38
|
+
def self.mock_controller
|
39
|
+
mock = MicroMock.make.new
|
40
|
+
mock.class.send :include, Coast
|
41
|
+
mock.stub(:authorize!) { |*args| @authorize_invoked = true }
|
42
|
+
mock.stub(:authorize_invoked?) { @authorize_invoked }
|
43
|
+
mock.stub(:respond_to) { |&block| @responded = true; block.call(format) }
|
44
|
+
mock.stub(:responded?) { @responded }
|
45
|
+
mock.stub(:render) { |*args| @performed = @rendered = true }
|
46
|
+
mock.stub(:performed?) { @performed }
|
47
|
+
mock.stub(:rendered?) { @rendered }
|
48
|
+
mock.stub(:redirect_to) { |*args| @redirected = true; render }
|
49
|
+
mock.stub(:redirected?) { @redirected }
|
50
|
+
mock.stub(:request) { @request ||= TestCoast.mock_request }
|
51
|
+
mock.stub(:params) { request.params }
|
52
|
+
mock.stub(:flash) { request.flash }
|
53
|
+
mock.stub(:format) { @format ||= TestCoast.mock_format }
|
54
|
+
mock.stub(:root_url) { "/" }
|
55
|
+
mock
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a mock format instance [ActionController::MimeResponds::Collector].
|
59
|
+
def self.mock_format
|
60
|
+
mock = MicroMock.make.new
|
61
|
+
mock.stub(:html) { |&block| @html = true; block.call if block }
|
62
|
+
mock.stub(:xml) { |&block| @xml = true; block.call if block }
|
63
|
+
mock.stub(:json) { |&block| @json = true; block.call if block }
|
64
|
+
mock
|
65
|
+
end
|
66
|
+
|
67
|
+
before do
|
68
|
+
@model = TestCoast.mock_model
|
69
|
+
@controller = TestCoast.mock_controller
|
70
|
+
@controller.class.set_resourceful_model @model.class
|
71
|
+
end
|
72
|
+
|
73
|
+
test ".set_localized" do
|
74
|
+
@controller.class.set_localized true
|
75
|
+
assert @controller.class.localized?
|
76
|
+
end
|
77
|
+
|
78
|
+
test ".localized=" do
|
79
|
+
@controller.class.localized = true
|
80
|
+
assert @controller.class.localized?
|
81
|
+
end
|
82
|
+
|
83
|
+
test ".set_authorize_method" do
|
84
|
+
@controller.class.set_authorize_method :authorize!
|
85
|
+
assert @controller.class.authorize_method == :authorize!
|
86
|
+
end
|
87
|
+
|
88
|
+
test ".authorize=" do
|
89
|
+
@controller.class.authorize_method = :authorize!
|
90
|
+
assert @controller.class.authorize_method == :authorize!
|
91
|
+
end
|
92
|
+
|
93
|
+
test ".set_resourceful_model" do
|
94
|
+
model = Object.new
|
95
|
+
@controller.class.set_resourceful_model model
|
96
|
+
assert @controller.class.resourceful_model == model
|
97
|
+
end
|
98
|
+
|
99
|
+
test ".resourceful_model=" do
|
100
|
+
model = Object.new
|
101
|
+
@controller.class.resourceful_model = model
|
102
|
+
assert @controller.class.resourceful_model == model
|
103
|
+
end
|
104
|
+
|
105
|
+
test "set before callbacks" do
|
106
|
+
RESTFUL_METHODS.each do |method|
|
107
|
+
assert !@controller.respond_to?("before_#{method}")
|
108
|
+
@controller.class.before(method) {}
|
109
|
+
assert @controller.respond_to?("before_#{method}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
test "set respond_to callbacks" do
|
114
|
+
RESTFUL_METHODS.each do |method|
|
115
|
+
assert !@controller.respond_to?("respond_to_#{method}")
|
116
|
+
@controller.class.respond_to(method) {}
|
117
|
+
assert @controller.respond_to?("respond_to_#{method}")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
test "set after callbacks" do
|
122
|
+
RESTFUL_METHODS.each do |method|
|
123
|
+
assert !@controller.respond_to?("after_#{method}")
|
124
|
+
@controller.class.after(method) {}
|
125
|
+
assert @controller.respond_to?("after_#{method}")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
test "RESTful methods exist" do
|
130
|
+
RESTFUL_METHODS.each do |method|
|
131
|
+
assert @controller.respond_to?(method)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
test "<create> flash message" do
|
136
|
+
@controller.create
|
137
|
+
assert @controller.flash[:notice].ends_with?("was successfully created.")
|
138
|
+
end
|
139
|
+
|
140
|
+
test "<create> localized flash message" do
|
141
|
+
@controller.class.localized = true
|
142
|
+
@controller.create
|
143
|
+
assert @controller.flash[:notice] =~ /translation missing: .+was_successfully_created/
|
144
|
+
end
|
145
|
+
|
146
|
+
test "<update> flash message" do
|
147
|
+
@controller.update
|
148
|
+
assert @controller.flash[:notice].ends_with?("was successfully updated.")
|
149
|
+
end
|
150
|
+
|
151
|
+
test "<update> localized flash message" do
|
152
|
+
@controller.class.localized = true
|
153
|
+
@controller.update
|
154
|
+
assert @controller.flash[:notice] =~ /translation missing: .+was_successfully_updated/
|
155
|
+
end
|
156
|
+
|
157
|
+
test "<destroy> flash message" do
|
158
|
+
@controller.destroy
|
159
|
+
assert @controller.flash[:notice].ends_with?("was successfully destroyed.")
|
160
|
+
end
|
161
|
+
|
162
|
+
test "<destroy> localized flash message" do
|
163
|
+
@controller.class.localized = true
|
164
|
+
@controller.destroy
|
165
|
+
assert @controller.flash[:notice] =~ /translation missing: .+was_successfully_destroyed/
|
166
|
+
end
|
167
|
+
|
168
|
+
RESTFUL_METHODS.each do |method|
|
169
|
+
|
170
|
+
test "<#{method}> responds and renders" do
|
171
|
+
@controller.send(method)
|
172
|
+
assert @controller.responded?
|
173
|
+
assert @controller.rendered?
|
174
|
+
end
|
175
|
+
|
176
|
+
test "<#{method}> renders for the formats html, xml, json" do
|
177
|
+
@controller.send(method)
|
178
|
+
assert(@controller.format.instance_eval { @html })
|
179
|
+
assert(@controller.format.instance_eval { @xml })
|
180
|
+
assert(@controller.format.instance_eval { @json })
|
181
|
+
end
|
182
|
+
|
183
|
+
test "<#{method}> invokes before, respond_to, after callbacks" do
|
184
|
+
callbacks = []
|
185
|
+
@controller.class.before(method) { callbacks << :before }
|
186
|
+
@controller.class.respond_to(method) { callbacks << :respond_to }
|
187
|
+
@controller.class.after(method) { callbacks << :after }
|
188
|
+
@controller.send(method)
|
189
|
+
assert callbacks.include?(:before)
|
190
|
+
assert callbacks.include?(:respond_to)
|
191
|
+
assert callbacks.include?(:after)
|
192
|
+
end
|
193
|
+
|
194
|
+
test "<#{method}> allows :respond_to callback to perform the render" do
|
195
|
+
@controller.class.respond_to(method) { @performed = true }
|
196
|
+
@controller.send(method)
|
197
|
+
assert @controller.performed?
|
198
|
+
assert !@controller.responded?
|
199
|
+
end
|
200
|
+
|
201
|
+
test "<#{method}> invokes the authorize_method when set" do
|
202
|
+
@controller.class.authorize_method = :authorize!
|
203
|
+
@controller.send(method)
|
204
|
+
assert @controller.authorize_invoked?
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
ITEM_METHODS.each do |method|
|
210
|
+
test "<#{method}> allows :before callback to set the resourceful_item" do
|
211
|
+
item = TestCoast.mock_model
|
212
|
+
@controller.class.before(method) { @resourceful_item = item }
|
213
|
+
@controller.send(method)
|
214
|
+
assert @controller.instance_eval { @resourceful_item.object_id } == item.object_id
|
215
|
+
end
|
216
|
+
|
217
|
+
test "<#{method}> sets a custom named instance variable for the item" do
|
218
|
+
item = TestCoast.mock_model
|
219
|
+
@controller.class.before(method) { @resourceful_item = item }
|
220
|
+
@controller.send(method)
|
221
|
+
variable = @controller.instance_eval { instance_variable_get(item_instance_var_name) }
|
222
|
+
assert variable.object_id == item.object_id
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
MUTATE_METHODS.each do |method|
|
227
|
+
test "<#{method}> redirects" do
|
228
|
+
@controller.send(method)
|
229
|
+
assert @controller.redirected?
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
test "<index> allows :before callback to set the list" do
|
234
|
+
list = TestCoast.mock_model.class.all
|
235
|
+
@controller.class.before(:index) { @resourceful_list = list }
|
236
|
+
@controller.index
|
237
|
+
assert @controller.instance_eval{ @resourceful_list }.object_id == list.object_id
|
238
|
+
end
|
239
|
+
|
240
|
+
test "<index> sets a custom named instance variable for the item" do
|
241
|
+
list = TestCoast.mock_model.class.all
|
242
|
+
@controller.class.before(:index) { @resourceful_list = list }
|
243
|
+
@controller.index
|
244
|
+
variable = @controller.instance_eval { instance_variable_get(list_instance_var_name) }
|
245
|
+
assert variable.object_id == list.object_id
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,16 +21,104 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: micro_test
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.3.0.rc4
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.3.0.rc4
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: micro_mock
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.0.5
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.0.5
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: pry-stack_explorer
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: awesome_print
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Resourceful behavior for Rails controllers with a simple DSL.
|
26
111
|
email:
|
27
|
-
-
|
112
|
+
- natehop@gmail.com
|
28
113
|
executables: []
|
29
114
|
extensions: []
|
30
115
|
extra_rdoc_files: []
|
31
116
|
files:
|
32
117
|
- lib/coast.rb
|
33
|
-
-
|
118
|
+
- lib/coast/version.rb
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- test/test_coast.rb
|
34
122
|
homepage: https://github.com/hopsoft/coast
|
35
123
|
licenses:
|
36
124
|
- MIT
|
@@ -52,9 +140,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
140
|
version: '0'
|
53
141
|
requirements: []
|
54
142
|
rubyforge_project:
|
55
|
-
rubygems_version: 1.8.
|
143
|
+
rubygems_version: 1.8.23
|
56
144
|
signing_key:
|
57
145
|
specification_version: 3
|
58
|
-
summary:
|
146
|
+
summary: A small mixin for Rails controllers that provides restful behavior.
|
59
147
|
test_files:
|
60
|
-
-
|
148
|
+
- test/test_coast.rb
|
149
|
+
has_rdoc:
|
data/spec/coast_spec.rb
DELETED
@@ -1,245 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
|
3
|
-
# fix MiniTest issue with 1.9
|
4
|
-
unless defined? Gem::Deprecate
|
5
|
-
module Gem
|
6
|
-
Deprecate = Module.new do
|
7
|
-
include Deprecate
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
require "bundler/setup"
|
13
|
-
require "minitest/autorun"
|
14
|
-
require "active_support/all"
|
15
|
-
require "turn"
|
16
|
-
require "pry"
|
17
|
-
|
18
|
-
dirname = File.dirname(__FILE__)
|
19
|
-
require File.join(dirname, "mock")
|
20
|
-
Dir.glob(File.join(dirname, "..", "lib", "*.rb")) { |f| require f }
|
21
|
-
MODEL_PATH = File.join(dirname, "testable_model.rb")
|
22
|
-
CONTROLLER_PATH = File.join(dirname, "testable_controller.rb")
|
23
|
-
load MODEL_PATH
|
24
|
-
load CONTROLLER_PATH
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
describe Coast do
|
30
|
-
RESTFUL_METHODS = %w(index show new edit create update destroy)
|
31
|
-
ITEM_METHODS = %w(show new edit create update destroy)
|
32
|
-
UI_METHODS = %w(new edit)
|
33
|
-
READ_METHODS = %w(index show)
|
34
|
-
MUTATE_METHODS = %w(create update destroy)
|
35
|
-
|
36
|
-
# Resets the mock class defs.
|
37
|
-
def reset
|
38
|
-
Coast.send(:remove_const, :TestableController)
|
39
|
-
Coast.send(:remove_const, :TestableModel)
|
40
|
-
load MODEL_PATH
|
41
|
-
load CONTROLLER_PATH
|
42
|
-
Coast::TestableController.set_resourceful_model Coast::TestableModel
|
43
|
-
end
|
44
|
-
|
45
|
-
# class method tests ============================================================================
|
46
|
-
|
47
|
-
describe :set_authorize_method do
|
48
|
-
it "sets the method used to perform authorization checks" do
|
49
|
-
reset
|
50
|
-
Coast::TestableController.set_authorize_method :authorize!
|
51
|
-
assert Coast::TestableController.authorize_method == :authorize!
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe :authorize_method= do
|
56
|
-
it "sets the method used to perform authorization checks" do
|
57
|
-
reset
|
58
|
-
Coast::TestableController.authorize_method = :authorize!
|
59
|
-
assert Coast::TestableController.authorize_method == :authorize!
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe :set_resourceful_model do
|
64
|
-
it "sets the model that the controller manages" do
|
65
|
-
reset
|
66
|
-
model = Object.new
|
67
|
-
Coast::TestableController.set_resourceful_model model
|
68
|
-
assert Coast::TestableController.resourceful_model == model
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe :resourceful_model= do
|
73
|
-
it "sets the model that the controller manages" do
|
74
|
-
reset
|
75
|
-
model = Object.new
|
76
|
-
Coast::TestableController.resourceful_model = model
|
77
|
-
assert Coast::TestableController.resourceful_model == model
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def verify_callback_setter(event, action)
|
82
|
-
reset
|
83
|
-
Coast::TestableController.send(event, action, &(lambda {}))
|
84
|
-
assert Coast::TestableController.new.respond_to?("#{event}_#{action}")
|
85
|
-
end
|
86
|
-
|
87
|
-
describe :before do
|
88
|
-
it "stores a before callback for all RESTful actions" do
|
89
|
-
RESTFUL_METHODS.each { |m| verify_callback_setter :before, m }
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe :respond_to do
|
94
|
-
it "stores a respond_to callback for all RESTful actions" do
|
95
|
-
RESTFUL_METHODS.each { |m| verify_callback_setter :respond_to, m }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
describe :after do
|
100
|
-
it "stores an after callback for all RESTful actions" do
|
101
|
-
RESTFUL_METHODS.each { |m| verify_callback_setter :after, m }
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
# instance method tests =========================================================================
|
116
|
-
|
117
|
-
|
118
|
-
it "supports all RESTful methods" do
|
119
|
-
reset
|
120
|
-
controller = Coast::TestableController.new
|
121
|
-
RESTFUL_METHODS.each { |m| controller.must_respond_to m }
|
122
|
-
end
|
123
|
-
|
124
|
-
# %w(index show new).each do |method|
|
125
|
-
RESTFUL_METHODS.each do |method|
|
126
|
-
describe "##{method}" do
|
127
|
-
before do
|
128
|
-
reset
|
129
|
-
end
|
130
|
-
|
131
|
-
it "responds and renders" do
|
132
|
-
controller = Coast::TestableController.new
|
133
|
-
controller.send(method)
|
134
|
-
assert controller.responded?, "Did not respond"
|
135
|
-
assert controller.rendered?, "Did not render"
|
136
|
-
end
|
137
|
-
|
138
|
-
it "renders for the formats html, xml, json" do
|
139
|
-
controller = Coast::TestableController.new
|
140
|
-
controller.send(method)
|
141
|
-
assert controller.html, "Did not respond to the html format"
|
142
|
-
assert controller.xml, "Did not respond to the xml format"
|
143
|
-
assert controller.json, "Did not respond to the json format"
|
144
|
-
end
|
145
|
-
|
146
|
-
it "invokes the callbacks before, respond_to, after" do
|
147
|
-
callbacks = []
|
148
|
-
|
149
|
-
Coast::TestableController.class_eval do
|
150
|
-
before(method) { callbacks << :before }
|
151
|
-
respond_to(method) { callbacks << :respond_to }
|
152
|
-
after(method) { callbacks << :after }
|
153
|
-
end
|
154
|
-
|
155
|
-
controller = Coast::TestableController.new
|
156
|
-
controller.send(method)
|
157
|
-
assert callbacks.include?(:before), "Did not invoke the before callback"
|
158
|
-
assert callbacks.include?(:respond_to), "Did not invoke the respond_to callback"
|
159
|
-
assert callbacks.include?(:after), "Did not invoke the after callback"
|
160
|
-
end
|
161
|
-
|
162
|
-
it "allows :respond_to callback to perform the render" do
|
163
|
-
Coast::TestableController.respond_to(method) { @performed = true }
|
164
|
-
controller = Coast::TestableController.new
|
165
|
-
controller.send(method)
|
166
|
-
assert controller.responded? == false, "Did not allow :respond_to callback to perform the render"
|
167
|
-
end
|
168
|
-
|
169
|
-
it "invokes the authorize_method when set" do
|
170
|
-
Coast::TestableController.authorize_method = :authorize!
|
171
|
-
controller = Coast::TestableController.new
|
172
|
-
controller.send(:index)
|
173
|
-
assert controller.authorize_invoked?, "Did not invoke the authorize_method"
|
174
|
-
end
|
175
|
-
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
ITEM_METHODS.each do |method|
|
180
|
-
describe "##{method}" do
|
181
|
-
before do
|
182
|
-
reset
|
183
|
-
end
|
184
|
-
|
185
|
-
it "allows :before callback to set the item" do
|
186
|
-
item = Object.new
|
187
|
-
Coast::TestableController.before(:index) { @resourceful_item = item }
|
188
|
-
controller = Coast::TestableController.new
|
189
|
-
controller.index
|
190
|
-
assert controller.instance_eval { @resourceful_item } == item, "Did not allow :before callback to set the resourceful_item"
|
191
|
-
end
|
192
|
-
|
193
|
-
it "sets a custom named instance variable for the item" do
|
194
|
-
item = Object.new
|
195
|
-
Coast::TestableController.before(:index) { @resourceful_item = item }
|
196
|
-
controller = Coast::TestableController.new
|
197
|
-
controller.index
|
198
|
-
variable = controller.instance_eval { instance_variable_get(item_instance_var_name) }
|
199
|
-
assert variable != nil, "Did not set a custom instance variable for the item"
|
200
|
-
assert variable == item, "Did not set a custom instance variable for the item to the correct value"
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
MUTATE_METHODS.each do |method|
|
207
|
-
describe "##{method}" do
|
208
|
-
before do
|
209
|
-
reset
|
210
|
-
end
|
211
|
-
|
212
|
-
it "redirects" do
|
213
|
-
controller = Coast::TestableController.new
|
214
|
-
controller.send(method)
|
215
|
-
assert controller.redirected?, "Did not redirect"
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
describe "#index" do
|
222
|
-
before do
|
223
|
-
reset
|
224
|
-
end
|
225
|
-
|
226
|
-
it "allows :before callback to set the list" do
|
227
|
-
list = [ Object.new ]
|
228
|
-
Coast::TestableController.before(:index) { @resourceful_list = list }
|
229
|
-
controller = Coast::TestableController.new
|
230
|
-
controller.index
|
231
|
-
assert controller.instance_eval { @resourceful_list } == list, "Did not allow :before callback to set the resourceful_list"
|
232
|
-
end
|
233
|
-
|
234
|
-
it "sets a custom named instance variable for the item" do
|
235
|
-
list = [ Object.new ]
|
236
|
-
Coast::TestableController.before(:index) { @resourceful_list = list }
|
237
|
-
controller = Coast::TestableController.new
|
238
|
-
controller.index
|
239
|
-
variable = controller.instance_eval { instance_variable_get(list_instance_var_name) }
|
240
|
-
assert variable != nil, "Did not set a custom instance variable for the item"
|
241
|
-
assert variable == list, "Did not set a custom instance variable for the item to the correct value"
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|