resourcerer 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fca34cdbd0b9675cd1e3dc66a1e664acca9c3c45
4
+ data.tar.gz: 64565620b02df20d0988d258973f3cc1e843c7eb
5
+ SHA512:
6
+ metadata.gz: 2f6bc395bf6917acfb7030d6275ab4f77d559d0a5c7bda6ac8ece7b9618fce610137f06e6c1dd17f440b42f0c1669bc548d4f9dd2489bd0d67e144d7f522d0f5
7
+ data.tar.gz: bb1d46a0030aa19625c4391511af787d4eab12a95161f946fc5c4c84ddbe0ef75c934cec8723fb5e77dac14532f7bbbf8af567718c65d15f54d8e86b07e63823
data/README.md ADDED
@@ -0,0 +1,258 @@
1
+ Resourcerer
2
+ =====================
3
+ What `resourcerer` proposes is that you go from this:
4
+
5
+ ```ruby
6
+ class Controller
7
+ def new
8
+ @person = Person.new
9
+ end
10
+
11
+ def create
12
+ @person = Person.new(person_params)
13
+ if @person.save
14
+ redirect_to(@person)
15
+ else
16
+ render :new
17
+ end
18
+ end
19
+
20
+ def edit
21
+ @person = Person.find(params[:id])
22
+ end
23
+
24
+ def update
25
+ @person = Person.find(params[:id])
26
+ if @person.update_attributes(person_params)
27
+ redirect_to(@person)
28
+ else
29
+ render :edit
30
+ end
31
+ end
32
+
33
+ private
34
+ def person_params
35
+ params.require(:person).permit(:name)
36
+ end
37
+ end
38
+ ```
39
+
40
+ To something like this:
41
+
42
+ ```ruby
43
+ class Controller
44
+ resource :person
45
+
46
+ def create
47
+ if person.save
48
+ redirect_to(person)
49
+ else
50
+ render :new
51
+ end
52
+ end
53
+
54
+ def update
55
+ if person.save
56
+ redirect_to(person)
57
+ else
58
+ render :edit
59
+ end
60
+ end
61
+
62
+ private
63
+ def person_params
64
+ params.require(:person).permit(:name)
65
+ end
66
+ end
67
+ ```
68
+
69
+ The idea is that you don't have to write boilerplate for standard CRUD actions, while at the same time improving your controllers readibility.
70
+
71
+ ## Usage
72
+
73
+ Let's see what Resourcerer is doing behind the curtains :smiley:. This examples assume that you are using Rails 4 Strong Parameters.
74
+
75
+ ### Obtaining a resource:
76
+
77
+ ```ruby
78
+ resource :person
79
+ ```
80
+
81
+ **Query Explanation**
82
+
83
+ <table>
84
+ <tr>
85
+ <td><code>id</code> present?</td>
86
+ <td>Query (get/delete)</td>
87
+ <td>Query (post/patch/put)</td>
88
+ </tr>
89
+ <tr>
90
+ <td><code>true</code></td>
91
+ <td><code>Person.find(params[:id])</code></td>
92
+ <td><code>Person.find(params[:id]).attributes = person_params</code></td>
93
+ </tr>
94
+ <tr>
95
+ <td><code>false</code></td>
96
+ <td><code>Person.new</code></td>
97
+ <td><code>Person.new(person_params)</code></td>
98
+ </tr>
99
+ </table>
100
+
101
+
102
+ ### Configuration
103
+
104
+ Let's take a look at some of the things you can do:
105
+
106
+ **Specify the model name:**
107
+
108
+ ```ruby
109
+ resource(:company, model: :enterprise)
110
+ ```
111
+
112
+ **Specify the parameter key to use to fetch the object:**
113
+
114
+ ```ruby
115
+ resource(:enterprise, finder_param: :company_id)
116
+ ```
117
+
118
+ **Specify how to obtain the object attributes:**
119
+
120
+ ```ruby
121
+ # Specify the strong parameters method's name when using the default `StrongParametersStrategy`
122
+ resource(:employee, attributes_method: :person_params)
123
+
124
+ # Specify the parameter key that holds the attributes when using the `EagerAttributesStrategy`
125
+ resource(:person, param_key: :employee)
126
+ ```
127
+
128
+ ### Setting a distinct object for a single action
129
+
130
+ There are times when one action in a controller is different from the
131
+ rest of the actions. A nice approach to circumvent this is to use the
132
+ controller's setter methods. This example uses [presenter_rails](https://github.com/ElMassimo/presenter_rails).
133
+
134
+ ```ruby
135
+ resource(:article)
136
+
137
+ def show_oldest
138
+ self.article = Article.find_oldest
139
+ end
140
+
141
+ present :article do
142
+ ArticlePresenter.new(article)
143
+ end
144
+ ```
145
+
146
+ ### Custom strategies
147
+
148
+ For times when you need custom behavior for resource finding, you can
149
+ create your own strategy by extending `Resourcerer::Strategy`:
150
+
151
+ ```ruby
152
+ class VerifiableStrategy < Resourcerer::Strategy
153
+ delegate :current_user, :to => :controller
154
+
155
+ def resource
156
+ instance = model.find(params[:id])
157
+ if current_user != instance.user
158
+ raise ActiveRecord::RecordNotFound
159
+ end
160
+ instance
161
+ end
162
+ end
163
+ ```
164
+
165
+ You would then use your custom strategy in your controller:
166
+
167
+ ```ruby
168
+ resource(:post, strategy: VerifiableStrategy)
169
+ ```
170
+
171
+ ## Using decorators or presenters
172
+ ### With [draper](http://github.com/drapergem/draper)
173
+
174
+ If you use decorators, you can go from something like this:
175
+
176
+ ```ruby
177
+ class Controller
178
+ def new
179
+ @person = Person.new.decorate
180
+ end
181
+
182
+ def create
183
+ @person = Person.new(person_params)
184
+ if @person.save
185
+ redirect_to(@person)
186
+ else
187
+ @person = @person.decorate
188
+ render :new
189
+ end
190
+ end
191
+
192
+ def edit
193
+ @person = Person.find(params[:id]).decorate
194
+ end
195
+
196
+ def update
197
+ @person = Person.find(params[:id])
198
+ if @person.update_attributes(person_params)
199
+ redirect_to(@person)
200
+ else
201
+ @person = @person.decorate
202
+ render :edit
203
+ end
204
+ end
205
+
206
+ private
207
+ def person_params
208
+ params.require(:person).permit(:name)
209
+ end
210
+ end
211
+ ```
212
+
213
+ To something like this by adding [presenter_rails](https://github.com/ElMassimo/presenter_rails) to the mix:
214
+
215
+ ```ruby
216
+ class Controller
217
+ resource(:person)
218
+
219
+ present :person do
220
+ person.decorate
221
+ end
222
+
223
+ def create
224
+ if person.save
225
+ redirect_to(person)
226
+ else
227
+ render :new
228
+ end
229
+ end
230
+
231
+ def update
232
+ if person.save
233
+ redirect_to(person)
234
+ else
235
+ render :edit
236
+ end
237
+ end
238
+
239
+ private
240
+ def person_params
241
+ params.require(:person).permit(:name)
242
+ end
243
+ end
244
+ ```
245
+
246
+ ### Comparison with [decent_exposure](https://github.com/voxdolo/decent_exposure).
247
+
248
+ Resourcerer is heavily inspired on [decent exposure](https://github.com/voxdolo/decent_exposure), it attempts to be more predictable by focusing on finding a resource and assigning attributes, and discarding completely the view exposure part.
249
+
250
+ #### Similarities
251
+ Both allow you to find or initialize a resource and assign attributes, removing the boilerplate from most CRUD actions.
252
+
253
+ #### Differences
254
+ Resourcerer does not expose an object to the view in any way, scope the query to a collection method if defined, nor deal with collections.
255
+
256
+
257
+ ### Special Thanks
258
+ Resourcerer was inspired by [decent_exposure](https://github.com/voxdolo/decent_exposure).
@@ -0,0 +1,33 @@
1
+ require 'active_support/inflector'
2
+ require 'active_support/core_ext/string'
3
+ require 'active_model/naming'
4
+
5
+ module Resourcerer
6
+ class Inflector
7
+ attr_reader :original, :model, :finder_attribute
8
+
9
+ def initialize(name, model, finder_attribute=:id)
10
+ @original, @model, @finder_attribute = name.to_s, model, finder_attribute
11
+ end
12
+
13
+ alias name original
14
+
15
+ def model_name
16
+ @model_name ||= model.model_name
17
+ rescue
18
+ @model_name = ActiveModel::Name.new(model)
19
+ end
20
+
21
+ def param_key
22
+ model_name.param_key
23
+ end
24
+
25
+ def finder_param
26
+ "#{model_name.singular}_#{finder_attribute}"
27
+ end
28
+
29
+ def self.class_for(name)
30
+ name.to_s.split('::').map(&:classify).join('::').constantize
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'resourcerer/inflector'
2
+ require 'resourcerer/strategies/strong_parameters_strategy'
3
+
4
+ module Resourcerer
5
+ class Resource
6
+ attr_reader :name, :strategy, :options, :config_proc
7
+
8
+ def self.for(name, options={}, config_proc=nil)
9
+ strategy_class = options.delete(:strategy) || Strategies::StrongParametersStrategy
10
+ new strategy_class, name, options, config_proc
11
+ end
12
+
13
+ def initialize(strategy, name, options, config_proc=nil)
14
+ @strategy, @name, @options, @config_proc = strategy, name, options, config_proc
15
+ end
16
+
17
+ def call(controller)
18
+ strategy.new(controller, name, options, config_proc).resource
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,45 @@
1
+ # This is the base class that is intended to build the DSL for Resourcer
2
+ module Resourcerer
3
+ class ResourceConfiguration
4
+
5
+ attr_reader :options
6
+
7
+ def initialize(options={})
8
+ @options = options || {}
9
+ end
10
+
11
+ def attributes
12
+ @attributes ||= options[:attributes].try(:call)
13
+ end
14
+
15
+ def method_missing(name, *args)
16
+ super if args.size > 1
17
+ define_option_method(name)
18
+ send(name, args.first)
19
+ end
20
+
21
+ def define_option_method(name)
22
+ # We chose not caching the value inside the method because it's more flexible
23
+ self.class.class_eval <<-EVAL
24
+ def #{name}(value=nil)
25
+ options[:#{name}] = value if value
26
+ options[:#{name}]
27
+ end
28
+ EVAL
29
+ end
30
+
31
+ # DSL
32
+
33
+ def find(&block)
34
+ options[:finder] = block
35
+ end
36
+
37
+ def build(&block)
38
+ options[:builder] = block
39
+ end
40
+
41
+ def assign(&block)
42
+ options[:attributes] = block
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ require 'resourcerer/resource'
2
+ require 'simple_memoizer'
3
+
4
+ module Resourcerer
5
+ module Resourceable
6
+ extend ActiveSupport::Concern
7
+ include SimpleMemoizer
8
+
9
+ module ClassMethods
10
+
11
+ def _resources
12
+ @_resources ||= {}
13
+ end
14
+
15
+ def resource(name, options={}, &block)
16
+ check_method_available!(name)
17
+ _resources[name] = resource = Resource.for(name, options, block)
18
+ define_resource_method(name, resource)
19
+ end
20
+
21
+ private
22
+
23
+ def define_resource_method(name, resource)
24
+ define_method(name) { resource.call(self) }
25
+ memoize name
26
+ hide_action name, "#{name}="
27
+ end
28
+
29
+ def check_method_available!(name)
30
+ if self.respond_to?(name.to_sym)
31
+ Kernel.abort "[ERROR] You are adding a singular resource by the `#{name}` method, " \
32
+ "which overrides an existing method of the same name. " \
33
+ "Consider a different resource name\n" \
34
+ "#{caller.first}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ module Resourcerer
2
+ module Strategies
3
+ module AssignAttributes
4
+
5
+ def resource
6
+ super.tap do |r|
7
+ assign_attributes(r) if r && assign_attributes?
8
+ end
9
+ end
10
+
11
+ def attributes
12
+ config.attributes
13
+ end
14
+
15
+ def assign_attributes?
16
+ !get? && !delete? && attributes.present?
17
+ end
18
+
19
+ def assign_attributes(resource)
20
+ resource.attributes = attributes
21
+ end
22
+
23
+ def resource_params
24
+ params[param_key]
25
+ end
26
+
27
+ private
28
+
29
+ def param_key
30
+ config.param_key || inflector.param_key
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ require 'resourcerer/strategies/assign_attributes'
2
+
3
+ module Resourcerer
4
+ module Strategies
5
+ module AssignFromMethod
6
+ include AssignAttributes
7
+
8
+ def attributes
9
+ super || attributes_from_method
10
+ end
11
+
12
+ private
13
+
14
+ def attributes_from_method
15
+ controller.send(attributes_method) if resource_params
16
+ end
17
+
18
+ def attributes_method
19
+ config.attributes_method || "#{name}_params"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ require 'resourcerer/strategies/assign_attributes'
2
+
3
+ module Resourcerer
4
+ module Strategies
5
+ module AssignFromParams
6
+ include AssignAttributes
7
+
8
+ def attributes
9
+ super || resource_params
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ require 'resourcerer/strategy'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ module Resourcerer
5
+ module Strategies
6
+ class DefaultStrategy < Strategy
7
+ delegate :get?, :delete?, to: :request
8
+
9
+ def resource
10
+ if id
11
+ find_resource(id)
12
+ else
13
+ build_resource
14
+ end
15
+ end
16
+
17
+ def id
18
+ @id ||= params[finder_param] || params[finder_attribute]
19
+ end
20
+
21
+ def finder_param
22
+ config.finder_param || inflector.finder_param
23
+ end
24
+
25
+ def find_resource(id)
26
+ config.finder.try(:call, id) || model.find_by(finder_attribute => id)
27
+ end
28
+
29
+ def build_resource
30
+ config.builder.try(:call) || model.new
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'resourcerer/strategies/default_strategy'
2
+ require 'resourcerer/strategies/assign_from_params'
3
+
4
+ module Resourcerer
5
+ module Strategies
6
+ class EagerAttributesStrategy < DefaultStrategy
7
+ include Strategies::AssignFromParams
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'resourcerer/strategies/default_strategy'
2
+ require 'resourcerer/strategies/assign_from_method'
3
+
4
+ module Resourcerer
5
+ module Strategies
6
+ class StrongParametersStrategy < DefaultStrategy
7
+ include Strategies::AssignFromMethod
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,55 @@
1
+ require 'resourcerer/inflector'
2
+ require 'resourcerer/resource_configuration'
3
+
4
+ module Resourcerer
5
+ class Strategy
6
+ attr_reader :controller, :name, :options, :config_proc
7
+ attr_writer :model, :inflector
8
+
9
+ def initialize(controller, name, options={}, config_proc=nil)
10
+ @controller, @name, @options, @config_proc = controller, name.to_s, options, config_proc
11
+ end
12
+
13
+ def resource
14
+ raise 'Implement in subclass'
15
+ end
16
+
17
+ protected
18
+
19
+ # Subclasses may provide an extended version of ResourceConfiguration to extend the DSL
20
+ def build_configuration(options)
21
+ ResourceConfiguration.new(options)
22
+ end
23
+
24
+ def config
25
+ @config ||= build_configuration(options).tap do |rc|
26
+ rc.instance_eval(&config_proc) if config_proc # This evaluates the configuration in the DSL block
27
+ end
28
+ end
29
+
30
+ def inflector
31
+ @inflector ||= Resourcerer::Inflector.new(name, model, finder_attribute)
32
+ end
33
+
34
+ def model
35
+ @model ||= case config.model
36
+ when Class, Module
37
+ config.model
38
+ else
39
+ Resourcerer::Inflector.class_for(config.model || name)
40
+ end
41
+ end
42
+
43
+ def finder_attribute
44
+ config.finder_attribute || :id
45
+ end
46
+
47
+ def params
48
+ controller.params
49
+ end
50
+
51
+ def request
52
+ controller.request
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ require 'resourcerer/resourceable'
2
+
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include Resourcerer::Resourceable
5
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resourcerer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Máximo Mussini
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: simple_memoizer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Extracted from decent exposure, allows you to define resources to automate
42
+ finding a record and assigning attributes.
43
+ email:
44
+ - maximomussini@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files:
48
+ - README.md
49
+ files:
50
+ - README.md
51
+ - lib/resourcerer.rb
52
+ - lib/resourcerer/inflector.rb
53
+ - lib/resourcerer/resource.rb
54
+ - lib/resourcerer/resource_configuration.rb
55
+ - lib/resourcerer/resourceable.rb
56
+ - lib/resourcerer/strategies/assign_attributes.rb
57
+ - lib/resourcerer/strategies/assign_from_method.rb
58
+ - lib/resourcerer/strategies/assign_from_params.rb
59
+ - lib/resourcerer/strategies/default_strategy.rb
60
+ - lib/resourcerer/strategies/eager_attributes_strategy.rb
61
+ - lib/resourcerer/strategies/strong_parameters_strategy.rb
62
+ - lib/resourcerer/strategy.rb
63
+ homepage: https://github.com/ElMassimo/resourcerer
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '2.0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Dry up your controllers by defining resources
88
+ test_files: []