singular_resource 0.0.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 +7 -0
- data/README.md +164 -0
- data/lib/singular_resource/configuration.rb +19 -0
- data/lib/singular_resource/error.rb +4 -0
- data/lib/singular_resource/finder.rb +17 -0
- data/lib/singular_resource/finder_strategizer.rb +13 -0
- data/lib/singular_resource/inflector.rb +27 -0
- data/lib/singular_resource/resource.rb +55 -0
- data/lib/singular_resource/strategies/assign_attributes.rb +20 -0
- data/lib/singular_resource/strategies/assign_from_method.rb +19 -0
- data/lib/singular_resource/strategies/assign_from_params.rb +19 -0
- data/lib/singular_resource/strategies/eager_attributes_strategy.rb +8 -0
- data/lib/singular_resource/strategies/mongoid_strategy.rb +25 -0
- data/lib/singular_resource/strategies/strong_parameters_strategy.rb +10 -0
- data/lib/singular_resource/strategy.rb +40 -0
- data/lib/singular_resource.rb +6 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3f053e2406cc78d4fe7039e2aa1d1aa66b52d120
|
4
|
+
data.tar.gz: a4ce51239058b9c8b948a2a033ee337f5b2481b5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4eac4db87ae958755b2f441de10a40a5d5016d8b6cd160c4504729bf9fcfd13089967fd86605d9683c698f10edc589adf08bc6015729654206d3d73c4580b94
|
7
|
+
data.tar.gz: 49b1a5f63fffbfab073923ad6a39b2eab8e0fc441c4cfdd4d1dcf621ddca7d2758d104aa7d11921453729322d6175286026914308143c86419e86b1cc667b14c
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
Singular Resource
|
2
|
+
=====================
|
3
|
+
|
4
|
+
Extracted from decent exposure, attempts to leave the useful parts, and just use `helper_method` to expose your view models.
|
5
|
+
|
6
|
+
## DOES
|
7
|
+
Allow you to find or initialize a simple resource, removing the boilerplate from CRUD actions.
|
8
|
+
|
9
|
+
|
10
|
+
## DOES NOT
|
11
|
+
Expose the model in any way, scope the query to a collection method if defined, or deal with collections.
|
12
|
+
|
13
|
+
|
14
|
+
## Use
|
15
|
+
It provides a private method that performs a query for the document when invoked, unless the id is not defined (`new`, `create`), in which case it returns an initialized model.
|
16
|
+
```ruby
|
17
|
+
singular_resource :patient
|
18
|
+
```
|
19
|
+
|
20
|
+
#### Strategies
|
21
|
+
Like `decent_exposure`, it's configurable, and provides different strategies.
|
22
|
+
By default, it uses `StrongParametersStrategy`, which only assigns the attributes if a method name is provided via the `attributes` option.
|
23
|
+
|
24
|
+
#### Options
|
25
|
+
``` ruby
|
26
|
+
:optional => "True if shouldn't fail if document does not exist",
|
27
|
+
|
28
|
+
:model => "Class or name of the model class",
|
29
|
+
|
30
|
+
:finder_parameter => "Name of the parameter that has the document's id",
|
31
|
+
|
32
|
+
:attributes => "Name of the attributes method name if using strong parameters",
|
33
|
+
|
34
|
+
:param_key => "Name of the parameter that has the document's attributes"
|
35
|
+
```
|
36
|
+
|
37
|
+
## Comparison
|
38
|
+
What `singular_resource` proposes is that you go from this:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class Controller
|
42
|
+
def new
|
43
|
+
@person = Person.new(params[:person])
|
44
|
+
end
|
45
|
+
|
46
|
+
def create
|
47
|
+
@person = Person.new(params[:person])
|
48
|
+
if @person.save
|
49
|
+
redirect_to(@person)
|
50
|
+
else
|
51
|
+
render :new
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def edit
|
56
|
+
@person = Person.find(params[:id])
|
57
|
+
end
|
58
|
+
|
59
|
+
def update
|
60
|
+
@person = Person.find(params[:id])
|
61
|
+
if @person.update_attributes(params[:person])
|
62
|
+
redirect_to(@person)
|
63
|
+
else
|
64
|
+
render :edit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
To something like this:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class Controller
|
74
|
+
expose(:person)
|
75
|
+
|
76
|
+
def create
|
77
|
+
if person.save
|
78
|
+
redirect_to(person)
|
79
|
+
else
|
80
|
+
render :new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def update
|
85
|
+
if person.save
|
86
|
+
redirect_to(person)
|
87
|
+
else
|
88
|
+
render :edit
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
### With [draper](http://github.com/drapergem/draper)
|
95
|
+
|
96
|
+
If you use decorators, you can go from something like this:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class Controller
|
100
|
+
def new
|
101
|
+
@person = Person.new(params[:person]).decorate
|
102
|
+
end
|
103
|
+
|
104
|
+
def create
|
105
|
+
@person = Person.new(params[:person])
|
106
|
+
if @person.save
|
107
|
+
redirect_to(@person)
|
108
|
+
else
|
109
|
+
@person = @person.decorate
|
110
|
+
render :new
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def edit
|
115
|
+
@person = Person.find(params[:id]).decorate
|
116
|
+
end
|
117
|
+
|
118
|
+
def update
|
119
|
+
@person = Person.find(params[:id])
|
120
|
+
if @person.update_attributes(params[:person])
|
121
|
+
redirect_to(@person)
|
122
|
+
else
|
123
|
+
@person = @person.decorate
|
124
|
+
render :edit
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
To something like this:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class Controller
|
134
|
+
before_filter :decorate_person
|
135
|
+
|
136
|
+
singular_resource(:person)
|
137
|
+
|
138
|
+
def create
|
139
|
+
if person.save
|
140
|
+
redirect_to(person)
|
141
|
+
else
|
142
|
+
render :new
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def update
|
147
|
+
if person.save
|
148
|
+
redirect_to(person)
|
149
|
+
else
|
150
|
+
render :edit
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def decorate_person
|
156
|
+
@person = person.decorate
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
If you think that the `before_filter` is nasty or don't like ivars in your views, so do I! Check the [present](http://github.com/ElMassimo/present) gem
|
162
|
+
|
163
|
+
### Special Thanks
|
164
|
+
Singular Resource is a subset of [decent_exposure](https://github.com/voxdolo/decent_exposure).
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SingularResource
|
2
|
+
class Configuration
|
3
|
+
def initialize(&block)
|
4
|
+
instance_exec(&block) if block_given?
|
5
|
+
end
|
6
|
+
|
7
|
+
def merge(other)
|
8
|
+
options.merge(other)
|
9
|
+
end
|
10
|
+
|
11
|
+
def options
|
12
|
+
@options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(key,value)
|
16
|
+
self.options[key] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'singular_resource/inflector'
|
2
|
+
|
3
|
+
module SingularResource
|
4
|
+
class Finder
|
5
|
+
attr_accessor :name, :strategy, :options
|
6
|
+
|
7
|
+
def initialize(name, strategy, options)
|
8
|
+
self.name = name.to_s
|
9
|
+
self.strategy = strategy
|
10
|
+
self.options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(controller)
|
14
|
+
strategy.new(controller, name, options).resource
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'singular_resource/finder'
|
2
|
+
require 'singular_resource/strategies/strong_parameters_strategy'
|
3
|
+
|
4
|
+
module SingularResource
|
5
|
+
class FinderStrategizer
|
6
|
+
|
7
|
+
def self.strategy_for(name, options={})
|
8
|
+
strategy_class = options.delete(:strategy) || Strategies::StrongParametersStrategy
|
9
|
+
options = options.merge(name: name)
|
10
|
+
Finder.new(name, strategy_class, options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
module SingularResource
|
5
|
+
class Inflector
|
6
|
+
attr_reader :original, :model
|
7
|
+
|
8
|
+
def initialize(name, model)
|
9
|
+
@original = name.to_s
|
10
|
+
@model = model
|
11
|
+
end
|
12
|
+
|
13
|
+
alias name original
|
14
|
+
|
15
|
+
def param_key
|
16
|
+
model.name.param_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def parameter
|
20
|
+
"#{model.name.singularize}_id"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.class_for(name)
|
24
|
+
name.to_s.classify.constantize
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'singular_resource/finder_strategizer'
|
2
|
+
require 'singular_resource/configuration'
|
3
|
+
require 'simple_memoizer'
|
4
|
+
|
5
|
+
module SingularResource
|
6
|
+
module Resource
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include SimpleMemoizer
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :_singular_configurations
|
12
|
+
self._singular_configurations ||= Hash.new(Configuration.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def singular_configuration(name=:default,&block)
|
17
|
+
self._singular_configurations = _singular_configurations.merge(name => Configuration.new(&block))
|
18
|
+
end
|
19
|
+
|
20
|
+
def singular_resource(name, options={})
|
21
|
+
enforce_method_name_not_used!(name)
|
22
|
+
|
23
|
+
config = options[:config] || :default
|
24
|
+
options = _singular_configurations[config].merge(options)
|
25
|
+
|
26
|
+
_resource_finders[name] = finder = FinderStrategizer.strategy_for(name, options)
|
27
|
+
|
28
|
+
define_resource_method(name, finder)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _resource_finders
|
34
|
+
@_resource_finders ||= {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_resource_method(name, finder)
|
38
|
+
define_method(name) do
|
39
|
+
finder.call(self)
|
40
|
+
end
|
41
|
+
memoize name
|
42
|
+
hide_action name
|
43
|
+
end
|
44
|
+
|
45
|
+
def enforce_method_name_not_used!(name)
|
46
|
+
if ActionController::Base.instance_methods.include?(name.to_sym)
|
47
|
+
Kernel.abort "[ERROR] You are adding a singular resource by the `#{name}` method, " \
|
48
|
+
"which overrides an existing ActionController method of the same name. " \
|
49
|
+
"Consider a different resource name\n" \
|
50
|
+
"#{caller.first}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SingularResource
|
2
|
+
module Strategies
|
3
|
+
module AssignAttributes
|
4
|
+
|
5
|
+
def attributes
|
6
|
+
raise 'Implement in submodule'
|
7
|
+
end
|
8
|
+
|
9
|
+
def assign_attributes?
|
10
|
+
!get? && !delete? && attributes.present?
|
11
|
+
end
|
12
|
+
|
13
|
+
def resource
|
14
|
+
super.tap do |r|
|
15
|
+
r.attributes = attributes if r && assign_attributes?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'singular_resource/strategies/assign_attributes'
|
2
|
+
|
3
|
+
module SingularResource
|
4
|
+
module Strategies
|
5
|
+
module AssignFromMethod
|
6
|
+
include AssignAttributes
|
7
|
+
|
8
|
+
def attributes
|
9
|
+
@attributes ||= method_attributes || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def method_attributes
|
15
|
+
controller.send(options[:attributes]) if options[:attributes]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'singular_resource/strategies/assign_attributes'
|
2
|
+
|
3
|
+
module SingularResource
|
4
|
+
module Strategies
|
5
|
+
module AssignFromParams
|
6
|
+
include AssignAttributes
|
7
|
+
|
8
|
+
def attributes
|
9
|
+
@attributes ||= params[param_key] || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def param_key
|
15
|
+
options[:param_key] || inflector.param_key
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'singular_resource/strategy'
|
2
|
+
require 'active_support/core_ext/module/delegation'
|
3
|
+
|
4
|
+
module SingularResource
|
5
|
+
class MongoidStrategy < Strategy
|
6
|
+
delegate :get?, :delete?, :to => :request
|
7
|
+
delegate :parameter, :to => :inflector
|
8
|
+
|
9
|
+
def id
|
10
|
+
params[parameter] || params[finder_parameter]
|
11
|
+
end
|
12
|
+
|
13
|
+
def finder_parameter
|
14
|
+
options[:finder_parameter] || :id
|
15
|
+
end
|
16
|
+
|
17
|
+
def resource
|
18
|
+
if id
|
19
|
+
options[:optional] ? model.where(id: id).first : model.find(id)
|
20
|
+
else
|
21
|
+
model.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'singular_resource/strategies/mongoid_strategy'
|
2
|
+
require 'singular_resource/strategies/assign_from_method'
|
3
|
+
|
4
|
+
module SingularResource
|
5
|
+
module Strategies
|
6
|
+
class StrongParametersStrategy < MongoidStrategy
|
7
|
+
include Strategies::AssignFromMethod
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'singular_resource/inflector'
|
2
|
+
|
3
|
+
module SingularResource
|
4
|
+
class Strategy
|
5
|
+
attr_reader :controller, :name, :options
|
6
|
+
attr_writer :model, :inflector
|
7
|
+
|
8
|
+
def initialize(controller, name, options={})
|
9
|
+
@controller, @name, @options = controller, name.to_s, options
|
10
|
+
end
|
11
|
+
|
12
|
+
def resource
|
13
|
+
raise 'Implement in subclass'
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def inflector
|
19
|
+
@inflector ||= SingularResource::Inflector.new(name, model)
|
20
|
+
end
|
21
|
+
|
22
|
+
def model
|
23
|
+
@model ||= case options[:model]
|
24
|
+
when Class, Module
|
25
|
+
options[:model]
|
26
|
+
else
|
27
|
+
name_or_model = options[:model] || name
|
28
|
+
SingularResource::Inflector.class_for(name_or_model)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def params
|
33
|
+
controller.params
|
34
|
+
end
|
35
|
+
|
36
|
+
def request
|
37
|
+
controller.request
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: singular_resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Máximo Mussini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-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, attempts to leave the useful parts, and
|
42
|
+
just use `helper_method` to expose your view models.
|
43
|
+
email:
|
44
|
+
- maximomussini@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files:
|
48
|
+
- README.md
|
49
|
+
files:
|
50
|
+
- README.md
|
51
|
+
- lib/singular_resource.rb
|
52
|
+
- lib/singular_resource/configuration.rb
|
53
|
+
- lib/singular_resource/error.rb
|
54
|
+
- lib/singular_resource/finder.rb
|
55
|
+
- lib/singular_resource/finder_strategizer.rb
|
56
|
+
- lib/singular_resource/inflector.rb
|
57
|
+
- lib/singular_resource/resource.rb
|
58
|
+
- lib/singular_resource/strategies/assign_attributes.rb
|
59
|
+
- lib/singular_resource/strategies/assign_from_method.rb
|
60
|
+
- lib/singular_resource/strategies/assign_from_params.rb
|
61
|
+
- lib/singular_resource/strategies/eager_attributes_strategy.rb
|
62
|
+
- lib/singular_resource/strategies/mongoid_strategy.rb
|
63
|
+
- lib/singular_resource/strategies/strong_parameters_strategy.rb
|
64
|
+
- lib/singular_resource/strategy.rb
|
65
|
+
homepage: https://github.com/ElMassimo/singular_resource
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --charset=UTF-8
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '2.0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.2.2
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Subset of decent_exposure, leaves the good parts and dismisses the 'magic'
|
90
|
+
test_files: []
|