decent_exposure 0.2.4 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/VERSION +1 -1
- data/lib/decent_exposure.rb +2 -1
- data/lib/decent_exposure/default_exposure.rb +42 -0
- data/lib/decent_exposure/railtie.rb +13 -9
- data/rails/init.rb +2 -14
- data/spec/lib/decent_exposure_spec.rb +46 -142
- data/spec/lib/rails_integration_spec.rb +130 -0
- metadata +25 -8
data/README.md
CHANGED
@@ -22,11 +22,11 @@ In `config/environment.rb`:
|
|
22
22
|
|
23
23
|
config.gem 'decent_exposure'
|
24
24
|
|
25
|
-
When used in Rails 3
|
25
|
+
When used in Rails 3:
|
26
26
|
|
27
27
|
In `Gemfile`:
|
28
28
|
|
29
|
-
gem 'decent_exposure'
|
29
|
+
gem 'decent_exposure'
|
30
30
|
|
31
31
|
|
32
32
|
The Particulars
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0.rc1
|
data/lib/decent_exposure.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
module DecentExposure
|
2
|
+
module DefaultExposure
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend(DecentExposure)
|
5
|
+
klass.superclass_delegating_accessor(:_default_exposure)
|
6
|
+
klass.default_exposure do |name|
|
7
|
+
self._resource_name = name.to_s
|
8
|
+
if id = params["#{name}_id"] || params[:id]
|
9
|
+
_proxy.find(id).tap do |r|
|
10
|
+
r.attributes = params[name] unless request.get?
|
11
|
+
end
|
12
|
+
else
|
13
|
+
_proxy.new(params[name])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
attr_accessor :_resource_name
|
20
|
+
|
21
|
+
def _resource_class
|
22
|
+
_resource_name.classify.constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
def _collection_name
|
26
|
+
_resource_name.pluralize
|
27
|
+
end
|
28
|
+
|
29
|
+
def _proxy
|
30
|
+
_collection.respond_to?(:scoped) ? _collection : _resource_class
|
31
|
+
end
|
32
|
+
|
33
|
+
def _collection
|
34
|
+
unless self.class.method_defined?(_collection_name)
|
35
|
+
self.class.expose(_collection_name) do
|
36
|
+
_collection_name.classify.constantize.scoped({})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
send(_collection_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,15 +1,19 @@
|
|
1
|
+
require 'decent_exposure/default_exposure'
|
2
|
+
|
1
3
|
module DecentExposure
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
default_exposure do |name|
|
8
|
-
model_class = name.to_s.classify.constantize
|
9
|
-
model_class.find(params["#{name}_id"] || params['id'])
|
4
|
+
if defined? Rails::Railtie
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
initializer "decent_exposure.extend_action_controller_base" do |app|
|
7
|
+
ActiveSupport.on_load(:action_controller) do
|
8
|
+
DecentExposure::Railtie.insert
|
10
9
|
end
|
11
10
|
end
|
12
11
|
end
|
13
12
|
end
|
14
|
-
end
|
15
13
|
|
14
|
+
class Railtie
|
15
|
+
def self.insert
|
16
|
+
ActionController::Base.send(:include, DecentExposure::DefaultExposure)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/rails/init.rb
CHANGED
@@ -1,14 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
rescue LoadError
|
4
|
-
require 'decent_exposure' # From gem
|
5
|
-
end
|
6
|
-
|
7
|
-
ActionController::Base.class_eval do
|
8
|
-
extend DecentExposure
|
9
|
-
superclass_delegating_accessor :_default_exposure
|
10
|
-
default_exposure do |name|
|
11
|
-
model_class = name.to_s.classify.constantize
|
12
|
-
model_class.find(params["#{name}_id"] || params['id'])
|
13
|
-
end
|
14
|
-
end
|
1
|
+
require 'decent_exposure/railtie'
|
2
|
+
DecentExposure::Railtie.insert
|
@@ -1,175 +1,79 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
|
3
|
-
|
4
|
-
extend DecentExposure
|
5
|
-
def self.helper_method(*args); end
|
6
|
-
def self.hide_action(*args); end
|
7
|
-
def memoizable(*args); args; end
|
8
|
-
expose(:proxy)
|
9
|
-
end
|
3
|
+
describe DecentExposure do
|
10
4
|
|
11
|
-
|
12
|
-
|
5
|
+
class Controller
|
6
|
+
extend DecentExposure
|
13
7
|
def self.helper_method(*args); end
|
14
8
|
def self.hide_action(*args); end
|
15
|
-
def
|
16
|
-
def params; {'resource_id' => 42}; end
|
9
|
+
def memoizable(arg); arg; end
|
17
10
|
end
|
18
|
-
end
|
19
|
-
require File.join(File.dirname(__FILE__), '..', '..', 'rails', 'init.rb')
|
20
|
-
|
21
|
-
class MyController < ActionController::Base
|
22
|
-
end
|
23
11
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
class Widget
|
29
|
-
def self.find(*args); end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe DecentExposure do
|
33
|
-
context "classes extending DecentExposure" do
|
34
|
-
it "respond to :expose" do
|
35
|
-
Quacker.respond_to?(:expose).should be_true
|
36
|
-
end
|
12
|
+
context 'classes extending DecentExposure' do
|
13
|
+
subject { Controller }
|
14
|
+
specify { should respond_to(:expose) }
|
15
|
+
specify { should respond_to(:default_exposure) }
|
37
16
|
end
|
38
17
|
|
39
|
-
context
|
40
|
-
let(:
|
41
|
-
|
42
|
-
it "creates a method with the given name" do
|
43
|
-
Quacker.new.methods.map{|m| m.to_s}.should include('proxy')
|
44
|
-
end
|
45
|
-
|
46
|
-
it "prevents the method from being a callable action" do
|
47
|
-
Quacker.expects(:hide_action).with(:blerg)
|
48
|
-
Quacker.class_eval do
|
49
|
-
expose(:blerg){ 'ehm' }
|
50
|
-
end
|
51
|
-
end
|
18
|
+
context '.expose' do
|
19
|
+
let(:controller) { Class.new(Controller){ expose(:resource) } }
|
20
|
+
let(:instance) { controller.new }
|
52
21
|
|
53
|
-
it
|
54
|
-
|
55
|
-
Quacker.expects(:helper_method).with(:blarg)
|
56
|
-
Quacker.class_eval do
|
57
|
-
expose(:blarg){ 'uhm' }
|
58
|
-
end
|
22
|
+
it 'creates a method with the given name' do
|
23
|
+
instance.methods.should include('resource')
|
59
24
|
end
|
60
25
|
|
61
|
-
it
|
62
|
-
|
63
|
-
|
64
|
-
Quacker.class_eval do
|
65
|
-
expose(:quack){ memoizable('quack!') }
|
66
|
-
end
|
67
|
-
instance.quack.should == %w(quack!)
|
26
|
+
it 'prevents the method from being a callable action' do
|
27
|
+
controller.expects(:hide_action).with(:resources)
|
28
|
+
controller.class_eval { expose(:resources) }
|
68
29
|
end
|
69
30
|
|
70
|
-
it
|
71
|
-
|
72
|
-
|
73
|
-
instance.quack
|
31
|
+
it 'declares the method as a helper method' do
|
32
|
+
controller.expects(:helper_method).with(:resources)
|
33
|
+
controller.class_eval { expose(:resources) }
|
74
34
|
end
|
75
35
|
|
76
|
-
context
|
77
|
-
before
|
78
|
-
|
79
|
-
|
80
|
-
def self.helper_method(*args); end
|
81
|
-
def self.hide_action(*args); end
|
82
|
-
def params; end
|
83
|
-
default_exposure {"default value"}
|
84
|
-
expose(:quack)
|
36
|
+
context 'custom exposures' do
|
37
|
+
before do
|
38
|
+
controller.class_eval do
|
39
|
+
expose(:resource) { memoizable("I'm a resource!") }
|
85
40
|
end
|
86
41
|
end
|
87
42
|
|
88
|
-
it
|
89
|
-
|
43
|
+
it 'returns the result of the exposed block from the method' do
|
44
|
+
instance.resource.should == "I'm a resource!"
|
90
45
|
end
|
91
46
|
|
92
|
-
it
|
93
|
-
|
94
|
-
|
95
|
-
expose :feathers
|
96
|
-
end
|
97
|
-
DuckController.new.feathers.should == "downy feathers"
|
47
|
+
it 'memoizes the value of the created method' do
|
48
|
+
instance.expects(:memoizable).once.returns('value')
|
49
|
+
2.times { instance.resource }
|
98
50
|
end
|
99
51
|
end
|
100
|
-
end
|
101
|
-
|
102
|
-
context "within Rails" do
|
103
|
-
let(:controller) {ActionController::Base.new}
|
104
|
-
|
105
|
-
let(:resource){ 'resource' }
|
106
|
-
let(:resource_class_name){ 'Resource' }
|
107
|
-
before do
|
108
|
-
resource.stubs(:to_s => resource, :classify => resource_class_name)
|
109
|
-
resource_class_name.stubs(:constantize => Resource)
|
110
|
-
end
|
111
|
-
|
112
|
-
it "extends ActionController::Base" do
|
113
|
-
ActionController::Base.respond_to?(:expose).should == true
|
114
|
-
end
|
115
52
|
|
116
|
-
context
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
ActionController::Base.class_eval do
|
121
|
-
expose name
|
122
|
-
end
|
123
|
-
controller.resource
|
124
|
-
end
|
125
|
-
context "or, when there is no :resource_id in params" do
|
53
|
+
context '.default_exposure' do
|
54
|
+
let(:defaulted_controller) { Class.new(Controller) }
|
55
|
+
let(:instance) { defaulted_controller.new }
|
56
|
+
context 'when the default_exposure is overridden' do
|
126
57
|
before do
|
127
|
-
|
128
|
-
|
58
|
+
defaulted_controller.class_eval do
|
59
|
+
default_exposure { 'default value' }
|
60
|
+
expose(:default)
|
129
61
|
end
|
130
62
|
end
|
131
|
-
it
|
132
|
-
|
133
|
-
controller.resource
|
63
|
+
it 'uses the overridden default_exposure' do
|
64
|
+
instance.default.should == 'default value'
|
134
65
|
end
|
135
66
|
end
|
136
|
-
end
|
137
|
-
|
138
|
-
let(:widget){ 'widget' }
|
139
|
-
let(:widget_class_name){ 'Widget' }
|
140
|
-
before do
|
141
|
-
widget.stubs(:to_s => widget, :classify => widget_class_name)
|
142
|
-
widget_class_name.stubs(:constantize => Widget)
|
143
|
-
end
|
144
|
-
|
145
|
-
let(:my_controller) {MyController.new}
|
146
|
-
|
147
|
-
it "works in descendant controllers" do
|
148
|
-
name = widget
|
149
|
-
Widget.expects(:find).with(123).returns('a widget')
|
150
|
-
MyController.class_eval do
|
151
|
-
def params; {'id' => 123} end
|
152
|
-
expose name
|
153
|
-
end
|
154
|
-
|
155
|
-
my_controller.widget.should == 'a widget'
|
156
|
-
end
|
157
|
-
|
158
|
-
it "allows overridden default in descendant controllers" do
|
159
|
-
MyController.class_eval do
|
160
|
-
default_exposure {|name| name.to_s}
|
161
|
-
expose :overridden
|
162
|
-
end
|
163
|
-
my_controller.overridden.should == 'overridden'
|
164
|
-
end
|
165
67
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
68
|
+
context 'with named arguments' do
|
69
|
+
it 'makes the named arguments available' do
|
70
|
+
defaulted_controller.class_eval do
|
71
|
+
default_exposure {|name| "I got: '#{name}'"}
|
72
|
+
expose :default
|
73
|
+
end
|
74
|
+
instance.default.should == "I got: 'default'"
|
75
|
+
end
|
171
76
|
end
|
172
|
-
controller.widget.should == 'preserved'
|
173
77
|
end
|
174
78
|
end
|
175
79
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'action_controller'
|
3
|
+
require 'decent_exposure/railtie'
|
4
|
+
DecentExposure::Railtie.insert
|
5
|
+
|
6
|
+
class Resource
|
7
|
+
def self.scoped(opts); self; end
|
8
|
+
def self.find(*args); end
|
9
|
+
def initialize(*args); end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Rails' integration:", DecentExposure do
|
13
|
+
let(:controller) { Class.new(ActionController::Base) }
|
14
|
+
let(:instance) { controller.new }
|
15
|
+
let(:request) { mock(:get? => true) }
|
16
|
+
let(:params) { HashWithIndifferentAccess.new(:resource_id => 42) }
|
17
|
+
|
18
|
+
before do
|
19
|
+
controller.expose(:resource)
|
20
|
+
instance.stubs(:request).returns(request)
|
21
|
+
end
|
22
|
+
|
23
|
+
context '.expose' do
|
24
|
+
it 'is available to ActionController::Base' do
|
25
|
+
ActionController::Base.should respond_to(:expose)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'within descendant controllers' do
|
30
|
+
let(:resource_controller) { Class.new(ActionController::Base) }
|
31
|
+
let(:instance) { resource_controller.new }
|
32
|
+
|
33
|
+
before do
|
34
|
+
instance.stubs(:request).returns(request)
|
35
|
+
instance.stubs(:params).returns(params)
|
36
|
+
resource_controller.expose :resource
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'inherits the default_exposure' do
|
40
|
+
Resource.stubs(:find).returns('resource')
|
41
|
+
instance.resource.should == 'resource'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'allows you to override the default_exposure' do
|
45
|
+
resource_controller.class_eval do
|
46
|
+
default_exposure {|name| name.to_s}
|
47
|
+
expose :overridden
|
48
|
+
end
|
49
|
+
instance.overridden.should == 'overridden'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does not override the default in ancestors' do
|
53
|
+
Resource.stubs(:find).returns('preserved')
|
54
|
+
instance.resource.should == 'preserved'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context '.default_exposure' do
|
59
|
+
before do
|
60
|
+
instance.stubs(:params).returns(params)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'is available to ActionController::Base' do
|
64
|
+
ActionController::Base.should respond_to(:default_exposure)
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when no collection method exists' do
|
68
|
+
it 'creates a collection method to scope from' do
|
69
|
+
instance.resource
|
70
|
+
instance.methods.should include('resources')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when a collection method exists' do
|
75
|
+
let(:controller){ Class.new(ActionController::Base) }
|
76
|
+
let(:instance){ controller.new }
|
77
|
+
|
78
|
+
before { class Person < Resource; end }
|
79
|
+
|
80
|
+
context 'and the collection can be scoped' do
|
81
|
+
let(:collection){ mock(:scoped => [self]) }
|
82
|
+
|
83
|
+
before{ controller.expose(:person) }
|
84
|
+
|
85
|
+
it 'uses the existing collection method' do
|
86
|
+
instance.stubs(:people).returns(collection)
|
87
|
+
collection.expects(:new)
|
88
|
+
instance.person
|
89
|
+
end
|
90
|
+
end
|
91
|
+
context 'when the collection can not be scoped' do
|
92
|
+
let(:collection){ mock }
|
93
|
+
|
94
|
+
before{ controller.expose(:person) }
|
95
|
+
|
96
|
+
it 'falls back to the singularized constant' do
|
97
|
+
instance.stubs(:people).returns(collection)
|
98
|
+
Person.expects(:new)
|
99
|
+
instance.person
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when either :resource_id or :id are present in params' do
|
105
|
+
it "calls find with params[:resource_id] on the resource's class" do
|
106
|
+
Resource.expects(:find).with(42)
|
107
|
+
instance.resource
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when there is no :resource_id in params' do
|
111
|
+
before { instance.stubs(:params).returns({:id => 73}) }
|
112
|
+
|
113
|
+
it "calls find with params[:id] on the resource's class" do
|
114
|
+
Resource.expects(:find).with(73)
|
115
|
+
instance.resource
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
context 'when there are no ids in params' do
|
120
|
+
before do
|
121
|
+
instance.stubs(:params).returns({:resource => {:name => 'bob'}})
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'calls new with params[:resouce_name]' do
|
125
|
+
Resource.expects(:new).with({:name => 'bob'})
|
126
|
+
instance.resource
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decent_exposure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: true
|
5
5
|
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
6
8
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.2.4
|
9
|
+
- rc1
|
10
|
+
version: 1.0.0.rc1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Stephen Caudill
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-07-
|
19
|
+
date: 2010-07-16 00:00:00 -04:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +47,18 @@ dependencies:
|
|
46
47
|
version: 0.9.8
|
47
48
|
type: :development
|
48
49
|
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: actionpack
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
49
62
|
description: "\n DecentExposure helps you program to an interface, rather than an implementation\n in your Rails controllers. The fact of the matter is that sharing state\n via instance variables in controllers promotes close coupling with views.\n DecentExposure gives you a declarative manner of exposing an interface to the\n state that controllers contain and thereby decreasing coupling and\n improving your testability and overall design.\n "
|
50
63
|
email: scaudill@gmail.com
|
51
64
|
executables: []
|
@@ -59,6 +72,7 @@ files:
|
|
59
72
|
- README.md
|
60
73
|
- VERSION
|
61
74
|
- lib/decent_exposure.rb
|
75
|
+
- lib/decent_exposure/default_exposure.rb
|
62
76
|
- lib/decent_exposure/railtie.rb
|
63
77
|
- rails/init.rb
|
64
78
|
has_rdoc: true
|
@@ -79,11 +93,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
93
|
version: "0"
|
80
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
95
|
requirements:
|
82
|
-
- - "
|
96
|
+
- - ">"
|
83
97
|
- !ruby/object:Gem::Version
|
84
98
|
segments:
|
85
|
-
-
|
86
|
-
|
99
|
+
- 1
|
100
|
+
- 3
|
101
|
+
- 1
|
102
|
+
version: 1.3.1
|
87
103
|
requirements: []
|
88
104
|
|
89
105
|
rubyforge_project:
|
@@ -94,3 +110,4 @@ summary: A helper for creating declarative interfaces in controllers
|
|
94
110
|
test_files:
|
95
111
|
- spec/helper.rb
|
96
112
|
- spec/lib/decent_exposure_spec.rb
|
113
|
+
- spec/lib/rails_integration_spec.rb
|