responder_controller 0.3.0 → 0.4.0
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/.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
|