resourcerer 0.2.1

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.
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: []