responder_controller 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/CHANGELOG +22 -5
- data/README.rdoc +10 -8
- data/Rakefile +6 -3
- data/VERSION +1 -1
- data/lib/responder_controller.rb +2 -1
- data/lib/responder_controller/actions.rb +5 -3
- data/lib/responder_controller/class_methods.rb +43 -31
- data/lib/responder_controller/instance_methods.rb +32 -21
- data/responder_controller.gemspec +27 -28
- data/spec/responder_controller/actions_spec.rb +2 -2
- data/spec/responder_controller/class_methods_spec.rb +25 -13
- data/spec/responder_controller/instance_methods_spec.rb +65 -43
- metadata +16 -7
- data/.gitignore +0 -21
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/CHANGELOG
CHANGED
@@ -1,8 +1,23 @@
|
|
1
|
+
*ResponderController 0.4.0*
|
2
|
+
|
3
|
+
* #find_model will call #from_param(params['id']) instead of #find on the
|
4
|
+
query returned by #find_models if that query responds to #from_param.
|
5
|
+
|
6
|
+
* Enable color in rspec
|
7
|
+
|
8
|
+
*ResponderController 0.3.0*
|
9
|
+
|
10
|
+
* Broke modules out into separate files.
|
11
|
+
* Actions#index calls #to_a on @models before passing to #respond_with. This
|
12
|
+
makes rails 3.0.0beta3 happy: otherwise "object references itself" errors
|
13
|
+
would occur in the json encoder.
|
14
|
+
|
1
15
|
*ResponderController 0.2.0*
|
2
16
|
|
3
|
-
* Added .serves_scopes, which takes :only and :except options to restrict the
|
4
|
-
record scopes that #index will serve. If a forbidden scope
|
5
|
-
raised, which will render as a 403 if
|
17
|
+
* Added .serves_scopes, which takes :only and :except options to restrict the
|
18
|
+
list of active record scopes that #index will serve. If a forbidden scope
|
19
|
+
is requested, ForbiddenScope will be raised, which will render as a 403 if
|
20
|
+
uncaught.
|
6
21
|
|
7
22
|
*ResponderController 0.1.3*
|
8
23
|
|
@@ -10,8 +25,10 @@
|
|
10
25
|
|
11
26
|
*ResponderController 0.1.2*
|
12
27
|
|
13
|
-
* If a requested scope errors out, raise a BadScope exception. Tell rails (if
|
28
|
+
* If a requested scope errors out, raise a BadScope exception. Tell rails (if
|
29
|
+
present) to render these as 422s.
|
14
30
|
|
15
31
|
*ResponderController 0.1.1*
|
16
32
|
|
17
|
-
* Use AR#scoped instead of #all to create initial query, as #all gives a
|
33
|
+
* Use AR#scoped instead of #all to create initial query, as #all gives a
|
34
|
+
literal array (whoops.)
|
data/README.rdoc
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
= responder_controller
|
2
2
|
|
3
|
-
Rails 3 responders wrap up as much crud controller code as possible without
|
4
|
-
models on your behalf. This is a sensible cut-off for
|
5
|
-
fair amount of duplicate code in crud
|
3
|
+
Rails 3 responders wrap up as much crud controller code as possible without
|
4
|
+
finding or mutating models on your behalf. This is a sensible cut-off for
|
5
|
+
framework support, but it still leaves a fair amount of duplicate code in crud
|
6
|
+
controllers. App developers are free to abstract more.
|
6
7
|
|
7
8
|
This is me abstracting more for my own apps. If it's handy for you, go nuts.
|
8
9
|
|
@@ -13,7 +14,7 @@ This is me abstracting more for my own apps. If it's handy for you, go nuts.
|
|
13
14
|
belongs_to :user
|
14
15
|
|
15
16
|
scope :authored_by, lambda { |user_id| where(:user_id => user_id) }
|
16
|
-
scope :recent, lambda { |
|
17
|
+
scope :recent, lambda { |num| order("updated_at DESC").limit(num.to_i) }
|
17
18
|
end
|
18
19
|
|
19
20
|
# app/controllers/posts_controller.rb
|
@@ -29,7 +30,7 @@ This is me abstracting more for my own apps. If it's handy for you, go nuts.
|
|
29
30
|
# Client-side
|
30
31
|
GET /posts.html # renders Post.authored_by(your_id)
|
31
32
|
GET /posts.html?recent=10 # renders Post.authored_by(your_id).recent(10)
|
32
|
-
GET /posts/1.html # renders post 1 if
|
33
|
+
GET /posts/1.html # renders post 1 if you authored it, or 404
|
33
34
|
PUT /posts/1.html # update same
|
34
35
|
DELETE /posts/1.html # or delete it
|
35
36
|
|
@@ -94,13 +95,14 @@ This is me abstracting more for my own apps. If it's handy for you, go nuts.
|
|
94
95
|
* Add tests for it. This is important so I don't break it in a
|
95
96
|
future version unintentionally.
|
96
97
|
* Commit, do not mess with rakefile, version, or history.
|
97
|
-
(if you want to have your own version, that is fine but bump version in a
|
98
|
-
ignore when I pull)
|
98
|
+
(if you want to have your own version, that is fine but bump version in a
|
99
|
+
commit by itself I can ignore when I pull)
|
99
100
|
* Send me a pull request. Bonus points for topic branches.
|
100
101
|
|
101
102
|
== Thanks
|
102
103
|
|
103
|
-
Thanks to SEOmoz (http://seomoz.org) for letting me build this at my desk in
|
104
|
+
Thanks to SEOmoz (http://seomoz.org) for letting me build this at my desk in
|
105
|
+
the afternoons instead of on the couch in the middle of the night ^_^.
|
104
106
|
|
105
107
|
== Copyright
|
106
108
|
|
data/Rakefile
CHANGED
@@ -6,17 +6,20 @@ begin
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "responder_controller"
|
8
8
|
gem.summary = %Q{like resources_controller, but for rails 3 responders}
|
9
|
-
gem.description =
|
9
|
+
gem.description =
|
10
|
+
%Q{Responders make crud controllers tiny, this wraps the rest.}
|
10
11
|
gem.email = "phil.h.smith@gmail.com"
|
11
12
|
gem.homepage = "http://github.com/phs/responder_controller"
|
12
13
|
gem.authors = ["Phil Smith"]
|
13
14
|
gem.add_dependency "activesupport", ">= 3.0.0.beta3"
|
14
15
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
|
-
# gem is a Gem::Specification...
|
16
|
+
# gem is a Gem::Specification...
|
17
|
+
# see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
18
|
end
|
17
19
|
Jeweler::GemcutterTasks.new
|
18
20
|
rescue LoadError
|
19
|
-
puts "Jeweler (or a dependency) not available.
|
21
|
+
puts "Jeweler (or a dependency) not available. " \
|
22
|
+
"Install it with: gem install jeweler"
|
20
23
|
end
|
21
24
|
|
22
25
|
require 'spec/rake/spectask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/responder_controller.rb
CHANGED
@@ -16,7 +16,8 @@ module ResponderController
|
|
16
16
|
class BadScope < ScopeError
|
17
17
|
end
|
18
18
|
|
19
|
-
# Raised when attempting to call a scope forbidden by
|
19
|
+
# Raised when attempting to call a scope forbidden by
|
20
|
+
# ClassMethods.serves_scopes.
|
20
21
|
#
|
21
22
|
# If this exception bubbles up, rails will render it as a 403.
|
22
23
|
class ForbiddenScope < ScopeError
|
@@ -15,8 +15,9 @@ module ResponderController
|
|
15
15
|
|
16
16
|
# Build (but do not save), assign and respond with a new model.
|
17
17
|
#
|
18
|
-
# The new model is built from the <tt>InstanceMethods#find_models</tt>
|
19
|
-
# could inherit any properties implied by those
|
18
|
+
# The new model is built from the <tt>InstanceMethods#find_models</tt>
|
19
|
+
# collection, meaning it could inherit any properties implied by those
|
20
|
+
# scopes.
|
20
21
|
def new
|
21
22
|
self.model = find_models.build
|
22
23
|
respond_with_contextual model
|
@@ -48,7 +49,8 @@ module ResponderController
|
|
48
49
|
respond_with_contextual model
|
49
50
|
end
|
50
51
|
|
51
|
-
# Find and destroy a model. Respond with
|
52
|
+
# Find and destroy a model. Respond with
|
53
|
+
# <tt>InstanceMethods#models_slug</tt>.
|
52
54
|
def destroy
|
53
55
|
find_model.destroy
|
54
56
|
respond_with_contextual models_slug
|
@@ -3,16 +3,18 @@ module ResponderController
|
|
3
3
|
module ClassMethods
|
4
4
|
# The underscored, fully-qualified name of the served model class.
|
5
5
|
#
|
6
|
-
# By default, it is the underscored controller class name, without
|
6
|
+
# By default, it is the underscored controller class name, without
|
7
|
+
# +_controller+.
|
7
8
|
def model_class_name
|
8
|
-
@model_class_name ||
|
9
|
+
@model_class_name || \
|
10
|
+
name.underscore.gsub(/_controller$/, '').singularize
|
9
11
|
end
|
10
12
|
|
11
13
|
# Declare the underscored, fully-qualified name of the served model class.
|
12
14
|
#
|
13
|
-
# Modules are declared with separating slashes, such as in
|
14
|
-
# or symbols are accepted, but other
|
15
|
-
# <tt>ArgumentError</tt>s.
|
15
|
+
# Modules are declared with separating slashes, such as in
|
16
|
+
# <tt>admin/setting</tt>. Strings or symbols are accepted, but other
|
17
|
+
# values (including actual classes) will raise <tt>ArgumentError</tt>s.
|
16
18
|
def serves_model(model_class_name)
|
17
19
|
unless model_class_name.is_a? String or model_class_name.is_a? Symbol
|
18
20
|
raise ArgumentError.new "Model must be a string or symbol"
|
@@ -23,13 +25,14 @@ module ResponderController
|
|
23
25
|
|
24
26
|
# Declare what active record scopes to allow or forbid to requests.
|
25
27
|
#
|
26
|
-
# .serves_scopes follows the regular :only/:except form: white-listed
|
27
|
-
# name as <tt>:only => [:allowed, :scopes]</tt> or
|
28
|
-
# black-listed ones are passed
|
28
|
+
# .serves_scopes follows the regular :only/:except form: white-listed
|
29
|
+
# scopes are passed by name as <tt>:only => [:allowed, :scopes]</tt> or
|
30
|
+
# <tt>:only => :just_one</tt>. Similarly, black-listed ones are passed
|
31
|
+
# under <tt>:except</tt>.
|
29
32
|
#
|
30
|
-
# If a white-list is passed, all other requested scopes (i.e. scopes named
|
31
|
-
# will be denied, raising <tt>ForbiddenScope</tt>.
|
32
|
-
# raise the exception.
|
33
|
+
# If a white-list is passed, all other requested scopes (i.e. scopes named
|
34
|
+
# by query parameters) will be denied, raising <tt>ForbiddenScope</tt>.
|
35
|
+
# If a black-list is passed, only they will raise the exception.
|
33
36
|
def serves_scopes(options = nil)
|
34
37
|
@serves_scopes ||= {}
|
35
38
|
|
@@ -38,7 +41,8 @@ module ResponderController
|
|
38
41
|
|
39
42
|
new_keys = @serves_scopes.keys | options.keys
|
40
43
|
unless new_keys == [:only] or new_keys == [:except]
|
41
|
-
raise ArgumentError.new(
|
44
|
+
raise ArgumentError.new(
|
45
|
+
"serves_scopes takes exactly one of :only and :except")
|
42
46
|
end
|
43
47
|
|
44
48
|
@serves_scopes[options.keys.first] ||= []
|
@@ -48,18 +52,21 @@ module ResponderController
|
|
48
52
|
@serves_scopes
|
49
53
|
end
|
50
54
|
|
51
|
-
# Declare leading arguments ("responder context") for
|
55
|
+
# Declare leading arguments ("responder context") for +#respond_with+.
|
52
56
|
#
|
53
|
-
# +respond_with+ creates urls from models. To avoid strongly coupling
|
54
|
-
# structure, it can take any number of leading parameters
|
55
|
-
#
|
57
|
+
# +respond_with+ creates urls from models. To avoid strongly coupling
|
58
|
+
# models to a url structure, it can take any number of leading parameters
|
59
|
+
# a la +#polymorphic_url+. +#responds_within+ declares these leading
|
60
|
+
# parameters, to be used on each +respond_with+ call.
|
56
61
|
#
|
57
62
|
# It takes either a varargs or a block, but not both. In
|
58
|
-
# InstanceMethods#respond_with_contextual, the blocks are called with
|
59
|
-
# the model (or models) as a parameter. They
|
63
|
+
# InstanceMethods#respond_with_contextual, the blocks are called with
|
64
|
+
# +#instance_exec+, taking the model (or models) as a parameter. They
|
65
|
+
# should return an array.
|
60
66
|
def responds_within(*args, &block)
|
61
67
|
if block and args.any?
|
62
|
-
raise ArgumentError.new(
|
68
|
+
raise ArgumentError.new(
|
69
|
+
"responds_within can take arguments or a block, but not both")
|
63
70
|
elsif block or args.any?
|
64
71
|
@responds_within ||= []
|
65
72
|
if not args.empty?
|
@@ -69,7 +76,8 @@ module ResponderController
|
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
72
|
-
@responds_within ||
|
79
|
+
@responds_within || \
|
80
|
+
model_class_name.split('/')[0...-1].collect { |m| m.to_sym }
|
73
81
|
end
|
74
82
|
|
75
83
|
# The served model class, identified by #model_class_name.
|
@@ -79,13 +87,14 @@ module ResponderController
|
|
79
87
|
|
80
88
|
# Declare a class-level scope for model collections.
|
81
89
|
#
|
82
|
-
# The model class is expected to respond to +all+, returning an Enumerable
|
83
|
-
# Declared scopes are applied to (and replace) this
|
84
|
-
# scopes.
|
90
|
+
# The model class is expected to respond to +all+, returning an Enumerable
|
91
|
+
# of models. Declared scopes are applied to (and replace) this
|
92
|
+
# collection, suitable for active record scopes.
|
85
93
|
#
|
86
|
-
# It takes one of a string, symbol or block. Symbols and strings are
|
87
|
-
# collection without arguments. Blocks are
|
88
|
-
#
|
94
|
+
# It takes one of a string, symbol or block. Symbols and strings are
|
95
|
+
# called as methods on the collection without arguments. Blocks are
|
96
|
+
# called with +#instance_exec+ taking the current, accumulated query and
|
97
|
+
# returning the new, scoped one.
|
89
98
|
def scope(*args, &block)
|
90
99
|
scope = args.first || block
|
91
100
|
|
@@ -102,10 +111,12 @@ module ResponderController
|
|
102
111
|
|
103
112
|
# Declare a (non-singleton) parent resource class.
|
104
113
|
#
|
105
|
-
# <tt>children_of 'accounts/user'</tt> implies a scope and some responder
|
106
|
-
# performs an ActiveRecord
|
107
|
-
#
|
108
|
-
#
|
114
|
+
# <tt>children_of 'accounts/user'</tt> implies a scope and some responder
|
115
|
+
# context. The scope performs an ActiveRecord
|
116
|
+
# <tt>where :user_id => params[:user_id]</tt>. The responder context is a
|
117
|
+
# call to <tt>#responds_within</tt> declaring the parent model's modules
|
118
|
+
# along with the parent itself, found with
|
119
|
+
# <tt>Accounts::User.find(params[:user_id])</tt>.
|
109
120
|
def children_of(parent_model_class_name)
|
110
121
|
parent_model_class_name = parent_model_class_name.to_s.underscore
|
111
122
|
|
@@ -118,7 +129,8 @@ module ResponderController
|
|
118
129
|
end
|
119
130
|
|
120
131
|
responds_within do
|
121
|
-
parent = parent_model_class_name.camelize.constantize.
|
132
|
+
parent = parent_model_class_name.camelize.constantize.
|
133
|
+
find params[parent_id]
|
122
134
|
parent_modules + [parent]
|
123
135
|
end
|
124
136
|
end
|
@@ -3,20 +3,21 @@ require 'active_support/core_ext/module/delegation'
|
|
3
3
|
module ResponderController
|
4
4
|
# Instance methods that support the Actions module.
|
5
5
|
module InstanceMethods
|
6
|
-
delegate :model_class_name, :model_class, :scopes, :responds_within,
|
7
|
-
:to => "self.class"
|
6
|
+
delegate :model_class_name, :model_class, :scopes, :responds_within,
|
7
|
+
:serves_scopes, :to => "self.class"
|
8
8
|
|
9
9
|
# Apply scopes to the given query.
|
10
10
|
#
|
11
|
-
# Applicable scopes come from two places. They are either declared at the
|
12
|
-
# <tt>ClassMethods#scope</tt>, or named in the request
|
13
|
-
# defining topics or enforcing security,
|
14
|
-
# clients.
|
11
|
+
# Applicable scopes come from two places. They are either declared at the
|
12
|
+
# class level with <tt>ClassMethods#scope</tt>, or named in the request
|
13
|
+
# itself. The former is good for defining topics or enforcing security,
|
14
|
+
# while the latter is free slicing and dicing for clients.
|
15
15
|
#
|
16
|
-
# Class-level scopes are applied first. Request scopes come after, and
|
17
|
-
# examining +params+. If any +params+ key matches a
|
18
|
-
# <tt>ClassMethods#model_class.scopes.keys</tt>, then it is
|
19
|
-
# applied. The values under that +params+ key
|
16
|
+
# Class-level scopes are applied first. Request scopes come after, and
|
17
|
+
# are discovered by examining +params+. If any +params+ key matches a
|
18
|
+
# name found in <tt>ClassMethods#model_class.scopes.keys</tt>, then it is
|
19
|
+
# taken to be a scope and is applied. The values under that +params+ key
|
20
|
+
# are passed along as arguments.
|
20
21
|
def scope(query)
|
21
22
|
query = (scopes || []).inject(query) do |query, scope|
|
22
23
|
if Symbol === scope and model_class.scopes.key? scope
|
@@ -28,9 +29,15 @@ module ResponderController
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
requested =
|
32
|
-
|
33
|
-
|
32
|
+
requested = model_class.scopes.keys & params.keys.collect(&:to_sym)
|
33
|
+
|
34
|
+
if serves_scopes[:only] && (requested - serves_scopes[:only]).any?
|
35
|
+
raise ForbiddenScope
|
36
|
+
end
|
37
|
+
|
38
|
+
if serves_scopes[:except] && (requested & serves_scopes[:except]).any?
|
39
|
+
raise ForbiddenScope
|
40
|
+
end
|
34
41
|
|
35
42
|
query = requested.inject(query) do |query, scope|
|
36
43
|
query.send scope, *params[scope.to_s] rescue raise BadScope
|
@@ -48,10 +55,12 @@ module ResponderController
|
|
48
55
|
|
49
56
|
# Find a particular model.
|
50
57
|
#
|
51
|
-
# #find_models is asked to find a model with <tt>params[:id]</tt>. This
|
52
|
-
# class-level scopes are enforced (potentially for security.)
|
58
|
+
# #find_models is asked to find a model with <tt>params[:id]</tt>. This
|
59
|
+
# ensures that class-level scopes are enforced (potentially for security.)
|
53
60
|
def find_model
|
54
|
-
find_models
|
61
|
+
models = find_models
|
62
|
+
finder = models.respond_to?(:from_param) ? :from_param : :find
|
63
|
+
models.send finder, params[:id]
|
55
64
|
end
|
56
65
|
|
57
66
|
# The underscored model class name, as a symbol.
|
@@ -98,11 +107,12 @@ module ResponderController
|
|
98
107
|
|
99
108
|
# Apply ClassMethods#responds_within to the given model (or symbol.)
|
100
109
|
#
|
101
|
-
# "Apply" just means turning +responds_within+ into an array and appending
|
102
|
-
# end. If +responds_within+ is an array, it used directly.
|
110
|
+
# "Apply" just means turning +responds_within+ into an array and appending
|
111
|
+
# +model+ to the end. If +responds_within+ is an array, it used directly.
|
103
112
|
#
|
104
|
-
# If it is a proc, it is called with +instance_exec+, passing +model+ in.
|
105
|
-
# array, which +model+ will be appended to. (So,
|
113
|
+
# If it is a proc, it is called with +instance_exec+, passing +model+ in.
|
114
|
+
# It should return an array, which +model+ will be appended to. (So,
|
115
|
+
# don't include it in the return value.)
|
106
116
|
def responder_context(model)
|
107
117
|
context = responds_within.collect do |o|
|
108
118
|
o = instance_exec model, &o if o.is_a? Proc
|
@@ -110,7 +120,8 @@ module ResponderController
|
|
110
120
|
end.flatten + [model]
|
111
121
|
end
|
112
122
|
|
113
|
-
# Pass +model+ through InstanceMethods#responder_context, and pass that to
|
123
|
+
# Pass +model+ through InstanceMethods#responder_context, and pass that to
|
124
|
+
# #respond_with.
|
114
125
|
def respond_with_contextual(model)
|
115
126
|
respond_with *responder_context(model)
|
116
127
|
end
|
@@ -1,59 +1,58 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{responder_controller}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Phil Smith"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-03}
|
13
13
|
s.description = %q{Responders make crud controllers tiny, this wraps the rest.}
|
14
14
|
s.email = %q{phil.h.smith@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
17
|
+
"README.rdoc"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
21
|
+
".rspec",
|
22
|
+
"CHANGELOG",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/responder_controller.rb",
|
28
|
+
"lib/responder_controller/actions.rb",
|
29
|
+
"lib/responder_controller/class_methods.rb",
|
30
|
+
"lib/responder_controller/instance_methods.rb",
|
31
|
+
"responder_controller.gemspec",
|
32
|
+
"spec/responder_controller/actions_spec.rb",
|
33
|
+
"spec/responder_controller/class_methods_spec.rb",
|
34
|
+
"spec/responder_controller/instance_methods_spec.rb",
|
35
|
+
"spec/responder_controller_spec.rb",
|
36
|
+
"spec/spec.opts",
|
37
|
+
"spec/spec_helper.rb"
|
38
38
|
]
|
39
39
|
s.homepage = %q{http://github.com/phs/responder_controller}
|
40
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
41
40
|
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = %q{1.3.
|
41
|
+
s.rubygems_version = %q{1.3.7}
|
43
42
|
s.summary = %q{like resources_controller, but for rails 3 responders}
|
44
43
|
s.test_files = [
|
45
44
|
"spec/responder_controller/actions_spec.rb",
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
"spec/responder_controller/class_methods_spec.rb",
|
46
|
+
"spec/responder_controller/instance_methods_spec.rb",
|
47
|
+
"spec/responder_controller_spec.rb",
|
48
|
+
"spec/spec_helper.rb"
|
50
49
|
]
|
51
50
|
|
52
51
|
if s.respond_to? :specification_version then
|
53
52
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
53
|
s.specification_version = 3
|
55
54
|
|
56
|
-
if Gem::Version.new(Gem::
|
55
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
56
|
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0.beta3"])
|
58
57
|
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
59
58
|
else
|
@@ -32,8 +32,8 @@ describe ResponderController do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it '#respond_with_contextual @models.to_a' do
|
35
|
-
@posts.should_receive(:to_a).and_return(:
|
36
|
-
@controller.should_receive(:respond_with_contextual).with(:
|
35
|
+
@posts.should_receive(:to_a).and_return(:posts)
|
36
|
+
@controller.should_receive(:respond_with_contextual).with(:posts)
|
37
37
|
@controller.index
|
38
38
|
end
|
39
39
|
end
|
@@ -132,7 +132,9 @@ describe ResponderController::ClassMethods do
|
|
132
132
|
end
|
133
133
|
|
134
134
|
it 'can specify a white list of active record scopes to serve' do
|
135
|
-
PostsController.serves_scopes :only => [
|
135
|
+
PostsController.serves_scopes :only => [
|
136
|
+
:recent, :authored_by, :published_after]
|
137
|
+
|
136
138
|
lambda do
|
137
139
|
@controller.scope @query
|
138
140
|
end.should raise_error(ResponderController::ForbiddenScope)
|
@@ -146,7 +148,9 @@ describe ResponderController::ClassMethods do
|
|
146
148
|
end
|
147
149
|
|
148
150
|
it 'can specify a black list of active record scopes to deny' do
|
149
|
-
PostsController.serves_scopes :except => [
|
151
|
+
PostsController.serves_scopes :except => [
|
152
|
+
:commented_on_by, :unpublished]
|
153
|
+
|
150
154
|
lambda do
|
151
155
|
@controller.scope @query
|
152
156
|
end.should raise_error(ResponderController::ForbiddenScope)
|
@@ -173,11 +177,12 @@ describe ResponderController::ClassMethods do
|
|
173
177
|
|
174
178
|
it 'whines when both :only and :except are passed' do
|
175
179
|
lambda do
|
176
|
-
PostsController.serves_scopes :only => :recent,
|
180
|
+
PostsController.serves_scopes :only => :recent,
|
181
|
+
:except => :commented_on_by
|
177
182
|
end.should raise_error ArgumentError
|
178
183
|
end
|
179
184
|
|
180
|
-
it 'whines if
|
185
|
+
it 'whines if :only and :except are passed between different calls' do
|
181
186
|
PostsController.serves_scopes :only => :recent
|
182
187
|
lambda do
|
183
188
|
PostsController.serves_scopes :except => :commented_on_by
|
@@ -202,7 +207,8 @@ describe ResponderController::ClassMethods do
|
|
202
207
|
end
|
203
208
|
|
204
209
|
it "takes, saves and returns a varargs" do
|
205
|
-
PostsController.responds_within(:foo, :bar, :baz).should ==
|
210
|
+
PostsController.responds_within(:foo, :bar, :baz).should == \
|
211
|
+
[:foo, :bar, :baz]
|
206
212
|
PostsController.responds_within.should == [:foo, :bar, :baz]
|
207
213
|
end
|
208
214
|
|
@@ -225,7 +231,8 @@ describe ResponderController::ClassMethods do
|
|
225
231
|
end
|
226
232
|
|
227
233
|
after :each do
|
228
|
-
|
234
|
+
# Clear out the garbage
|
235
|
+
PostsController.instance_variable_set "@responds_within", nil
|
229
236
|
end
|
230
237
|
end
|
231
238
|
|
@@ -238,28 +245,33 @@ describe ResponderController::ClassMethods do
|
|
238
245
|
PostsController.children_of 'accounts/user'.to_sym
|
239
246
|
end
|
240
247
|
|
241
|
-
it "creates a scope filtering by the parent
|
248
|
+
it "creates a scope filtering by the parent's key as passed in params" do
|
242
249
|
PostsController.children_of 'accounts/user'
|
243
250
|
controller = PostsController.new
|
244
251
|
controller.params[:user_id] = :the_user_id
|
245
252
|
|
246
|
-
|
247
|
-
|
253
|
+
@query.should_receive(:where).
|
254
|
+
with(:user_id => :the_user_id).
|
255
|
+
and_return(user_query = mock("user-restricted query"))
|
256
|
+
|
248
257
|
controller.scope(@query).should == user_query
|
249
258
|
end
|
250
259
|
|
251
|
-
it "adds a responds_within context, of the parent
|
260
|
+
it "adds a responds_within context, of the parent with its modules" do
|
252
261
|
PostsController.children_of 'accounts/user'
|
253
262
|
controller = PostsController.new
|
254
263
|
controller.params[:user_id] = :the_user_id
|
255
264
|
|
256
|
-
Accounts::User.should_receive(:find).with(:the_user_id).
|
265
|
+
Accounts::User.should_receive(:find).with(:the_user_id).
|
266
|
+
and_return(user = mock("the user"))
|
257
267
|
|
258
|
-
controller.responder_context(:argument).should == [
|
268
|
+
controller.responder_context(:argument).should == [
|
269
|
+
:accounts, user, :argument]
|
259
270
|
end
|
260
271
|
|
261
272
|
after :each do
|
262
|
-
|
273
|
+
# Clear out the garbage
|
274
|
+
PostsController.instance_variable_set "@responds_within", nil
|
263
275
|
PostsController.scopes.clear
|
264
276
|
end
|
265
277
|
end
|
@@ -35,56 +35,57 @@ describe ResponderController::InstanceMethods do
|
|
35
35
|
@query.stub!(:unpublished).and_return(@query)
|
36
36
|
@query.stub!(:recent).and_return(@query)
|
37
37
|
@query.stub!(:owned_by).with('me').and_return(@query)
|
38
|
+
|
39
|
+
@controller = PostsController.new
|
38
40
|
end
|
39
41
|
|
40
42
|
describe '#model_class_name' do
|
41
43
|
it "is .model_class_name" do
|
42
|
-
|
44
|
+
@controller.model_class_name.should == PostsController.model_class_name
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
48
|
describe '#model_class' do
|
47
49
|
it 'is .model_class' do
|
48
|
-
|
50
|
+
@controller.model_class.should == PostsController.model_class
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
52
54
|
describe '#scopes' do
|
53
55
|
it 'is .scopes' do
|
54
|
-
|
56
|
+
@controller.scopes.should == PostsController.scopes
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
describe '#scope' do
|
59
61
|
it "passes its argument out by default" do
|
60
|
-
|
62
|
+
@controller.scope(@query).should == @query
|
61
63
|
end
|
62
64
|
|
63
|
-
it "
|
65
|
+
it "sends explicit, declared scopes to the query in order" do
|
64
66
|
PostsController.scope :unpublished
|
65
67
|
PostsController.scope :recent
|
66
68
|
|
67
69
|
@query.should_receive(:unpublished).and_return(@query)
|
68
70
|
@query.should_receive(:recent).and_return(@query)
|
69
|
-
|
71
|
+
@controller.scope(@query).should == @query
|
70
72
|
end
|
71
73
|
|
72
|
-
it '
|
74
|
+
it '#instance_execs block scopes, passing in the query' do
|
73
75
|
PostsController.scope do |posts|
|
74
76
|
posts.owned_by(params[:user_id])
|
75
77
|
end
|
76
78
|
|
77
79
|
@query.should_receive(:owned_by).with('me').and_return(@query)
|
78
80
|
|
79
|
-
controller
|
80
|
-
controller.scope(@query).should == @query
|
81
|
+
@controller.scope(@query).should == @query
|
81
82
|
end
|
82
83
|
|
83
84
|
it 'explodes with an unknown scope' do
|
84
85
|
PostsController.scope :furst_p0sts
|
85
86
|
|
86
87
|
lambda do
|
87
|
-
|
88
|
+
@controller.scope
|
88
89
|
end.should raise_error ArgumentError
|
89
90
|
|
90
91
|
PostsController.scopes.pop
|
@@ -92,7 +93,6 @@ describe ResponderController::InstanceMethods do
|
|
92
93
|
|
93
94
|
context 'with request parameters naming scopes' do
|
94
95
|
before :each do
|
95
|
-
@controller = PostsController.new
|
96
96
|
@controller.params['commented_on_by'] = 'you'
|
97
97
|
end
|
98
98
|
|
@@ -102,10 +102,13 @@ describe ResponderController::InstanceMethods do
|
|
102
102
|
end
|
103
103
|
|
104
104
|
it 'is applied after class-level scopes' do
|
105
|
+
# owned_by is the last class-level scope
|
105
106
|
class_level_query = mock("class-level scoped query")
|
106
|
-
@query.should_receive(:owned_by).and_return(class_level_query)
|
107
|
+
@query.should_receive(:owned_by).and_return(class_level_query)
|
108
|
+
|
109
|
+
class_level_query.should_receive(:commented_on_by).
|
110
|
+
with('you').and_return(class_level_query)
|
107
111
|
|
108
|
-
class_level_query.should_receive(:commented_on_by).with('you').and_return(class_level_query)
|
109
112
|
@controller.scope(@query).should == class_level_query
|
110
113
|
end
|
111
114
|
|
@@ -120,28 +123,44 @@ describe ResponderController::InstanceMethods do
|
|
120
123
|
|
121
124
|
describe '#find_models' do
|
122
125
|
it 'is #scope #model_class.scoped' do
|
123
|
-
controller = PostsController.new
|
124
|
-
|
125
126
|
Post.should_receive(:scoped).and_return(@query)
|
126
|
-
controller.should_receive(:scope).with(@query).and_return(@query)
|
127
|
+
@controller.should_receive(:scope).with(@query).and_return(@query)
|
127
128
|
|
128
|
-
controller.find_models.should == @query
|
129
|
+
@controller.find_models.should == @query
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
132
133
|
describe '#find_model' do
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
134
|
+
before :each do
|
135
|
+
@post = mock("the post")
|
136
|
+
@query.stub!(:respond_to?).with(:from_param).and_return(false)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'is #from_param(params[:id]) if #find_models responds to it' do
|
140
|
+
@controller.should_receive(:find_models).and_return(@query)
|
141
|
+
|
142
|
+
@query.should_receive(:respond_to?).with(:from_param).and_return(true)
|
143
|
+
@query.should_receive(:from_param).
|
144
|
+
with(@controller.params[:id]).
|
145
|
+
and_return(@post)
|
146
|
+
|
147
|
+
@controller.find_model.should == @post
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'is #find_models.find(params[:id]) otherwise' do
|
151
|
+
@controller.should_receive(:find_models).and_return(@query)
|
152
|
+
|
153
|
+
@query.should_receive(:find).
|
154
|
+
with(@controller.params[:id]).
|
155
|
+
and_return(@post)
|
137
156
|
|
138
|
-
controller.find_model.should == post
|
157
|
+
@controller.find_model.should == @post
|
139
158
|
end
|
140
159
|
end
|
141
160
|
|
142
161
|
describe '#model_slug' do
|
143
162
|
it 'is the model class name' do
|
144
|
-
|
163
|
+
@controller.model_slug.should == :post
|
145
164
|
end
|
146
165
|
|
147
166
|
it 'drops the leading module names, if any' do
|
@@ -151,75 +170,78 @@ describe ResponderController::InstanceMethods do
|
|
151
170
|
|
152
171
|
describe '#models_slug' do
|
153
172
|
it 'is ths symbolized plural of #model_slug' do
|
154
|
-
|
173
|
+
@controller.models_slug.should == :posts
|
155
174
|
end
|
156
175
|
end
|
157
176
|
|
158
177
|
describe '#model_ivar' do
|
159
178
|
it 'is the #model_slug with a leading @' do
|
160
|
-
|
179
|
+
@controller.model_ivar.should == '@post'
|
161
180
|
end
|
162
181
|
end
|
163
182
|
|
164
183
|
describe '#models_ivar' do
|
165
184
|
it 'is the plural #model_ivar' do
|
166
|
-
|
185
|
+
@controller.models_ivar.should == @controller.model_ivar.pluralize
|
167
186
|
end
|
168
187
|
end
|
169
188
|
|
170
189
|
describe "#models" do
|
171
190
|
it "gets #models_ivar" do
|
172
|
-
|
173
|
-
controller.models.should == :some_posts
|
191
|
+
@controller.instance_variable_set("@posts", :some_posts)
|
192
|
+
@controller.models.should == :some_posts
|
174
193
|
end
|
175
194
|
end
|
176
195
|
|
177
196
|
describe "#model" do
|
178
197
|
it "gets #model_ivar" do
|
179
|
-
|
180
|
-
controller.model.should == :a_post
|
198
|
+
@controller.instance_variable_set("@post", :a_post)
|
199
|
+
@controller.model.should == :a_post
|
181
200
|
end
|
182
201
|
end
|
183
202
|
|
184
203
|
describe "#models=" do
|
185
204
|
it "assigns to #models_ivar" do
|
186
205
|
assigned = mock("some models")
|
187
|
-
|
188
|
-
controller.instance_variable_get("@posts").should == assigned
|
206
|
+
@controller.models = assigned
|
207
|
+
@controller.instance_variable_get("@posts").should == assigned
|
189
208
|
end
|
190
209
|
end
|
191
210
|
|
192
211
|
describe "#model=" do
|
193
212
|
it "assigns to #model_ivar" do
|
194
213
|
assigned = mock("a model")
|
195
|
-
|
196
|
-
controller.instance_variable_get("@post").should == assigned
|
214
|
+
@controller.model = assigned
|
215
|
+
@controller.instance_variable_get("@post").should == assigned
|
197
216
|
end
|
198
217
|
end
|
199
218
|
|
200
219
|
describe '#responds_within' do
|
201
220
|
it 'is .responds_within' do
|
202
|
-
|
221
|
+
@controller.responds_within.should == PostsController.responds_within
|
203
222
|
end
|
204
223
|
end
|
205
224
|
|
206
225
|
describe '#responder_context' do
|
207
226
|
it "is the argument prepended with responds_within" do
|
208
|
-
Admin::SettingsController.new.responder_context(:argument).should == [
|
227
|
+
Admin::SettingsController.new.responder_context(:argument).should == [
|
228
|
+
:admin, :argument]
|
209
229
|
end
|
210
230
|
|
211
|
-
it "passes
|
231
|
+
it "passes lambdas to responds_within and prepends the results" do
|
212
232
|
Admin::SettingsController.responds_within do |model|
|
213
233
|
model.should == :argument
|
214
234
|
[:nested, :namespace]
|
215
235
|
end
|
216
236
|
|
217
|
-
Admin::SettingsController.new.responder_context(:argument).should == [
|
237
|
+
Admin::SettingsController.new.responder_context(:argument).should == [
|
238
|
+
:nested, :namespace, :argument]
|
218
239
|
end
|
219
240
|
|
220
241
|
it "wraps the lambda result in an array if needed" do
|
221
242
|
Admin::SettingsController.responds_within { |model| :namespace }
|
222
|
-
Admin::SettingsController.new.responder_context(:argument).should == [
|
243
|
+
Admin::SettingsController.new.responder_context(:argument).should == [
|
244
|
+
:namespace, :argument]
|
223
245
|
end
|
224
246
|
|
225
247
|
after :each do
|
@@ -229,11 +251,11 @@ describe ResponderController::InstanceMethods do
|
|
229
251
|
|
230
252
|
describe '#respond_with_contextual' do
|
231
253
|
it 'passed #responder_context to #respond_with' do
|
232
|
-
controller
|
233
|
-
|
234
|
-
controller.should_receive(:respond_with).with(:contextualized_argument)
|
254
|
+
@controller.should_receive(:responder_context).
|
255
|
+
with(:argument).and_return([:contextualized_argument])
|
256
|
+
@controller.should_receive(:respond_with).with(:contextualized_argument)
|
235
257
|
|
236
|
-
controller.respond_with_contextual :argument
|
258
|
+
@controller.respond_with_contextual :argument
|
237
259
|
end
|
238
260
|
end
|
239
261
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: responder_controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
+
- 4
|
8
9
|
- 0
|
9
|
-
version: 0.
|
10
|
+
version: 0.4.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Phil Smith
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-01-03 00:00:00 -08:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: activesupport
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 299253627
|
27
30
|
segments:
|
28
31
|
- 3
|
29
32
|
- 0
|
@@ -36,9 +39,11 @@ dependencies:
|
|
36
39
|
name: rspec
|
37
40
|
prerelease: false
|
38
41
|
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
39
43
|
requirements:
|
40
44
|
- - ">="
|
41
45
|
- !ruby/object:Gem::Version
|
46
|
+
hash: 13
|
42
47
|
segments:
|
43
48
|
- 1
|
44
49
|
- 2
|
@@ -57,7 +62,7 @@ extra_rdoc_files:
|
|
57
62
|
- README.rdoc
|
58
63
|
files:
|
59
64
|
- .document
|
60
|
-
- .
|
65
|
+
- .rspec
|
61
66
|
- CHANGELOG
|
62
67
|
- LICENSE
|
63
68
|
- README.rdoc
|
@@ -79,28 +84,32 @@ homepage: http://github.com/phs/responder_controller
|
|
79
84
|
licenses: []
|
80
85
|
|
81
86
|
post_install_message:
|
82
|
-
rdoc_options:
|
83
|
-
|
87
|
+
rdoc_options: []
|
88
|
+
|
84
89
|
require_paths:
|
85
90
|
- lib
|
86
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
87
93
|
requirements:
|
88
94
|
- - ">="
|
89
95
|
- !ruby/object:Gem::Version
|
96
|
+
hash: 3
|
90
97
|
segments:
|
91
98
|
- 0
|
92
99
|
version: "0"
|
93
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
94
102
|
requirements:
|
95
103
|
- - ">="
|
96
104
|
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
97
106
|
segments:
|
98
107
|
- 0
|
99
108
|
version: "0"
|
100
109
|
requirements: []
|
101
110
|
|
102
111
|
rubyforge_project:
|
103
|
-
rubygems_version: 1.3.
|
112
|
+
rubygems_version: 1.3.7
|
104
113
|
signing_key:
|
105
114
|
specification_version: 3
|
106
115
|
summary: like resources_controller, but for rails 3 responders
|