coast 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.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
|
+

|
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
|