rails-patterns 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.
- checksums.yaml +4 -4
- data/Gemfile +4 -3
- data/Gemfile.lock +38 -0
- data/README.md +101 -2
- data/VERSION +1 -1
- data/lib/patterns/form.rb +100 -0
- data/lib/rails-patterns.rb +1 -0
- data/rails-patterns.gemspec +11 -3
- data/spec/patterns/form_spec.rb +432 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af39ea7538e043642610c2ea2ff35a9fa5c8a0e7
|
4
|
+
data.tar.gz: ff3231a21b94a92280acf664497a40b3039f7140
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3653e8ce337a6225ad14162600f8b2a462321fcca8595f8f1463920935749e17b48d3f7359530dff528bc7ff3f7175ddaa23b7ac1203903ff42b296bfb8c51d
|
7
|
+
data.tar.gz: 37f4e6cb0fc581140dfee9372386b9d37b77a092d63d798fb98d8617475ac249d2e8f49aa9d8092ae5630e3de5e48a0aa6677ed7d62e89429a788c9fb51e44f2
|
data/Gemfile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
|
3
|
+
gem "activerecord", ">= 4.2.6"
|
4
|
+
gem "actionpack", ">= 4.2.6"
|
5
|
+
gem "virtus"
|
5
6
|
|
6
7
|
# Add dependencies to develop your gem here.
|
7
8
|
# Include everything needed to run rake, tests, features, etc.
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
+
actionpack (5.0.2)
|
5
|
+
actionview (= 5.0.2)
|
6
|
+
activesupport (= 5.0.2)
|
7
|
+
rack (~> 2.0)
|
8
|
+
rack-test (~> 0.6.3)
|
9
|
+
rails-dom-testing (~> 2.0)
|
10
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
11
|
+
actionview (5.0.2)
|
12
|
+
activesupport (= 5.0.2)
|
13
|
+
builder (~> 3.1)
|
14
|
+
erubis (~> 2.7.0)
|
15
|
+
rails-dom-testing (~> 2.0)
|
16
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
4
17
|
activemodel (5.0.2)
|
5
18
|
activesupport (= 5.0.2)
|
6
19
|
activerecord (5.0.2)
|
@@ -14,12 +27,20 @@ GEM
|
|
14
27
|
tzinfo (~> 1.1)
|
15
28
|
addressable (2.4.0)
|
16
29
|
arel (7.1.4)
|
30
|
+
axiom-types (0.1.1)
|
31
|
+
descendants_tracker (~> 0.0.4)
|
32
|
+
ice_nine (~> 0.11.0)
|
33
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
17
34
|
builder (3.2.3)
|
18
35
|
coderay (1.1.1)
|
36
|
+
coercible (1.0.0)
|
37
|
+
descendants_tracker (~> 0.0.1)
|
19
38
|
concurrent-ruby (1.0.5)
|
20
39
|
descendants_tracker (0.0.4)
|
21
40
|
thread_safe (~> 0.3, >= 0.3.1)
|
22
41
|
diff-lcs (1.3)
|
42
|
+
equalizer (0.0.11)
|
43
|
+
erubis (2.7.0)
|
23
44
|
faraday (0.9.2)
|
24
45
|
multipart-post (>= 1.2, < 3)
|
25
46
|
git (1.3.0)
|
@@ -33,6 +54,7 @@ GEM
|
|
33
54
|
hashie (3.5.5)
|
34
55
|
highline (1.7.8)
|
35
56
|
i18n (0.8.1)
|
57
|
+
ice_nine (0.11.2)
|
36
58
|
juwelier (2.1.3)
|
37
59
|
builder
|
38
60
|
bundler (>= 1.13)
|
@@ -44,6 +66,8 @@ GEM
|
|
44
66
|
rdoc
|
45
67
|
semver
|
46
68
|
jwt (1.5.6)
|
69
|
+
loofah (2.0.3)
|
70
|
+
nokogiri (>= 1.5.9)
|
47
71
|
method_source (0.8.2)
|
48
72
|
mime-types (2.99.3)
|
49
73
|
mini_portile2 (2.1.0)
|
@@ -66,6 +90,13 @@ GEM
|
|
66
90
|
pry-rails (0.3.6)
|
67
91
|
pry (>= 0.10.4)
|
68
92
|
rack (2.0.1)
|
93
|
+
rack-test (0.6.3)
|
94
|
+
rack (>= 1.0)
|
95
|
+
rails-dom-testing (2.0.2)
|
96
|
+
activesupport (>= 4.2.0, < 6.0)
|
97
|
+
nokogiri (~> 1.6)
|
98
|
+
rails-html-sanitizer (1.0.3)
|
99
|
+
loofah (~> 2.0)
|
69
100
|
rake (12.0.0)
|
70
101
|
rdoc (5.1.0)
|
71
102
|
rspec (3.5.0)
|
@@ -86,16 +117,23 @@ GEM
|
|
86
117
|
thread_safe (0.3.6)
|
87
118
|
tzinfo (1.2.3)
|
88
119
|
thread_safe (~> 0.1)
|
120
|
+
virtus (1.0.5)
|
121
|
+
axiom-types (~> 0.1)
|
122
|
+
coercible (~> 1.0)
|
123
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
124
|
+
equalizer (~> 0.0, >= 0.0.9)
|
89
125
|
|
90
126
|
PLATFORMS
|
91
127
|
ruby
|
92
128
|
|
93
129
|
DEPENDENCIES
|
130
|
+
actionpack (>= 4.2.6)
|
94
131
|
activerecord (>= 4.2.6)
|
95
132
|
bundler (~> 1.0)
|
96
133
|
juwelier (~> 2.1.0)
|
97
134
|
pry-rails
|
98
135
|
rspec
|
136
|
+
virtus
|
99
137
|
|
100
138
|
BUNDLED WITH
|
101
139
|
1.14.6
|
data/README.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
A collection of lightweight, standardized, rails-oriented patterns.
|
4
4
|
|
5
|
+
- [Query - complex querying on active record relation](#query)
|
6
|
+
- [Service - useful for handling processes involving multiple steps](#service)
|
7
|
+
- [Collection - when in need to add a method that relates to the collection a whole](#collection)
|
8
|
+
- [Form - when you need a place for callbacks, want to replace strong parameters or handle virtual/composite resources](#form)
|
9
|
+
|
5
10
|
## Installation
|
6
11
|
|
7
12
|
```ruby
|
@@ -166,8 +171,102 @@ end
|
|
166
171
|
|
167
172
|
```ruby
|
168
173
|
ColorsCollection.new
|
169
|
-
|
170
|
-
|
174
|
+
CustomerEventsByTypeCollection.for(customer)
|
175
|
+
CustomerEventsByTypeCollection.for(customer, label_method: "name")
|
176
|
+
```
|
177
|
+
|
178
|
+
## Form
|
179
|
+
|
180
|
+
### When to use it
|
181
|
+
|
182
|
+
Form objects, just like service objects, are commonly used to mitigate problems with model callbacks that interact with external classes ([read more...](http://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/)).
|
183
|
+
Form objects can also be used as replacement for `ActionController::StrongParameters` strategy, as all writable attributes are re-defined within each form.
|
184
|
+
Finally form objects can be used as wrappers for virtual (with no model representation) or composite (saving multiple models at once) resources.
|
185
|
+
In the latter case this may act as replacement for `ActiveRecord::NestedAttributes`.
|
186
|
+
|
187
|
+
### Assumptions and rules
|
188
|
+
|
189
|
+
* Forms include `ActiveModel::Validations` to support validation.
|
190
|
+
* Forms include `Virtus.model` to support `attribute` static method with all [corresponding capabilities](https://github.com/solnic/virtus).
|
191
|
+
* Forms can be initialized using `.new`.
|
192
|
+
* Forms accept optional resource object as first constructor argument.
|
193
|
+
* Forms accept optional attributes hash as latter constructor argument.
|
194
|
+
* forms have to implement `#persist` method that returns falsey (if failed) or truthy (if succeeded) value.
|
195
|
+
* Forms provide access to first constructor argument using `#resource`.
|
196
|
+
* Forms are saved using their `#save` or `#save!` methods.
|
197
|
+
* Forms will attempt to pre-populate their fields using `resource#attributes` and public getters for `resource`
|
198
|
+
* Form's fields are populated with passed-in attributes hash reverse-merged with pre-populated attributes if possible.
|
199
|
+
* Forms provide `#as` builder method that populates internal `@form_owner` variable (can be used to store current user).
|
200
|
+
* Forms allow defining/overriding their `#param_key` method result by using `.param_key` static method. This defaults to `#resource#model_name#param_key`.
|
201
|
+
* Forms delegate `#persisted?` method to `#resource` if possible.
|
202
|
+
* Forms do handle `ActionController::Parameters` as attributes hash (using `to_unsafe_h`)
|
203
|
+
* It is recommended to wrap `#persist` method in transaction if possible and if multiple model are affected.
|
204
|
+
|
205
|
+
### Examples
|
206
|
+
|
207
|
+
#### Declaration
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
class UserForm < Patterns::Form
|
211
|
+
param_key "person"
|
212
|
+
|
213
|
+
attribute :first_name, String
|
214
|
+
attribute :last_name, String
|
215
|
+
attribute :age, Integer
|
216
|
+
attribute :full_address, String
|
217
|
+
attribute :skip_notification, Boolean
|
218
|
+
|
219
|
+
validate :first_name, :last_name, presence: true
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def persist
|
224
|
+
update_user and
|
225
|
+
update_address and
|
226
|
+
deliver_notification
|
227
|
+
end
|
228
|
+
|
229
|
+
def update_user
|
230
|
+
resource.update_attributes(attributes.except(:full_address, :skip_notification))
|
231
|
+
end
|
232
|
+
|
233
|
+
def update_address
|
234
|
+
resource.address.update_attributes(full_address: full_address)
|
235
|
+
end
|
236
|
+
|
237
|
+
def deliver_notification
|
238
|
+
skip_notification || UserNotifier.user_update_notification(user, form_owner).deliver
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class ReportConfigurationForm < Patterns::Form
|
243
|
+
param_key "report"
|
244
|
+
|
245
|
+
attribute :include_extra_data, Boolean
|
246
|
+
attribute :dump_as_csv, Boolean
|
247
|
+
attribute :comma_separated_column_names, String
|
248
|
+
attribute :date_start, Date
|
249
|
+
attribute :date_end, Date
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def persist
|
254
|
+
SendReport.call(attributes)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
#### Usage
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
form = UserForm.new(User.find(1), params[:person])
|
263
|
+
form.save
|
264
|
+
|
265
|
+
form = UserForm.new(User.new, params[:person]).as(current_user)
|
266
|
+
form.save!
|
267
|
+
|
268
|
+
ReportConfigurationForm.new
|
269
|
+
ReportConfigurationForm.new({ include_extra_data: true, dump_as_csv: true })
|
171
270
|
```
|
172
271
|
|
173
272
|
## Further reading
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "virtus"
|
2
|
+
require "action_controller/metal/strong_parameters"
|
3
|
+
|
4
|
+
module Patterns
|
5
|
+
class Form
|
6
|
+
include Virtus.model
|
7
|
+
include ActiveModel::Validations
|
8
|
+
|
9
|
+
Error = Class.new(StandardError)
|
10
|
+
Invalid = Class.new(Error)
|
11
|
+
NoParamKey = Class.new(Error)
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
attributes = args.extract_options!
|
15
|
+
|
16
|
+
if attributes.blank? && args.last.is_a?(ActionController::Parameters)
|
17
|
+
attributes = args.pop.to_unsafe_h
|
18
|
+
end
|
19
|
+
|
20
|
+
@resource = args.first
|
21
|
+
|
22
|
+
super(build_original_attributes.merge(attributes))
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
valid? ? persist : false
|
27
|
+
end
|
28
|
+
|
29
|
+
def save!
|
30
|
+
save.tap do |saved|
|
31
|
+
raise Invalid unless saved
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def as(form_owner)
|
36
|
+
@form_owner = form_owner
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_key
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_partial_path
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_model
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def persisted?
|
53
|
+
if resource&.respond_to?(:persisted?)
|
54
|
+
resource.persisted?
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def model_name
|
61
|
+
@model_name ||= Struct.
|
62
|
+
new(:param_key).
|
63
|
+
new(param_key)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.param_key(key = nil)
|
67
|
+
if key.nil?
|
68
|
+
@param_key
|
69
|
+
else
|
70
|
+
@param_key = key
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
attr_reader :resource, :form_owner
|
77
|
+
|
78
|
+
def param_key
|
79
|
+
param_key = self.class.param_key
|
80
|
+
param_key ||= resource&.respond_to?(:model_name) && resource.model_name.param_key
|
81
|
+
raise NoParamKey if param_key.blank?
|
82
|
+
param_key
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_original_attributes
|
86
|
+
return {} if resource.nil?
|
87
|
+
base_attributes = resource.respond_to?(:attributes) && resource.attributes.symbolize_keys
|
88
|
+
|
89
|
+
self.class.attribute_set.each_with_object(base_attributes || {}) do |attribute, result|
|
90
|
+
if result[attribute.name].blank? && resource.respond_to?(attribute.name)
|
91
|
+
result[attribute.name] = resource.public_send(attribute.name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def persist
|
97
|
+
raise NotImplementedError, "#persist has to be implemented"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/rails-patterns.rb
CHANGED
data/rails-patterns.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: rails-patterns 0.
|
5
|
+
# stub: rails-patterns 0.4.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "rails-patterns".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.4.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Stevo".freeze]
|
14
|
-
s.date = "2017-04-
|
14
|
+
s.date = "2017-04-21"
|
15
15
|
s.description = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
|
16
16
|
s.email = "b.kosmowski@selleo.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -29,11 +29,13 @@ Gem::Specification.new do |s|
|
|
29
29
|
"VERSION",
|
30
30
|
"lib/patterns.rb",
|
31
31
|
"lib/patterns/collection.rb",
|
32
|
+
"lib/patterns/form.rb",
|
32
33
|
"lib/patterns/query.rb",
|
33
34
|
"lib/patterns/service.rb",
|
34
35
|
"lib/rails-patterns.rb",
|
35
36
|
"rails-patterns.gemspec",
|
36
37
|
"spec/patterns/collection_spec.rb",
|
38
|
+
"spec/patterns/form_spec.rb",
|
37
39
|
"spec/patterns/query_spec.rb",
|
38
40
|
"spec/patterns/service_spec.rb",
|
39
41
|
"spec/spec_helper.rb"
|
@@ -48,17 +50,23 @@ Gem::Specification.new do |s|
|
|
48
50
|
|
49
51
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
52
|
s.add_runtime_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
|
53
|
+
s.add_runtime_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
|
54
|
+
s.add_runtime_dependency(%q<virtus>.freeze, [">= 0"])
|
51
55
|
s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
|
52
56
|
s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
53
57
|
s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
|
54
58
|
else
|
55
59
|
s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
|
60
|
+
s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
|
61
|
+
s.add_dependency(%q<virtus>.freeze, [">= 0"])
|
56
62
|
s.add_dependency(%q<rspec>.freeze, [">= 0"])
|
57
63
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
58
64
|
s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
|
59
65
|
end
|
60
66
|
else
|
61
67
|
s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
|
68
|
+
s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
|
69
|
+
s.add_dependency(%q<virtus>.freeze, [">= 0"])
|
62
70
|
s.add_dependency(%q<rspec>.freeze, [">= 0"])
|
63
71
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
64
72
|
s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
|
@@ -0,0 +1,432 @@
|
|
1
|
+
RSpec.describe Patterns::Form do
|
2
|
+
after { Object.send(:remove_const, :CustomForm) if defined?(CustomForm) }
|
3
|
+
|
4
|
+
it "includes Virtus.model" do
|
5
|
+
CustomForm = Class.new(Patterns::Form)
|
6
|
+
|
7
|
+
expect(CustomForm.attribute_set).to be_kind_of(Virtus::AttributeSet)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "includes ActiveModel::Validations" do
|
11
|
+
CustomForm = Class.new(Patterns::Form)
|
12
|
+
|
13
|
+
expect(CustomForm).to be < ActiveModel::Validations
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".new" do
|
17
|
+
it "returns form instance" do
|
18
|
+
CustomForm = Class.new(Patterns::Form)
|
19
|
+
|
20
|
+
form = CustomForm.new(double)
|
21
|
+
|
22
|
+
expect(form).to be_a_kind_of(CustomForm)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "assigns form attributes with values passed as second argument" do
|
26
|
+
CustomForm = Class.new(Patterns::Form) do
|
27
|
+
attribute :first_name, String
|
28
|
+
attribute :last_name, String
|
29
|
+
end
|
30
|
+
|
31
|
+
form = CustomForm.new({ first_name: "Tony", last_name: "Stark" })
|
32
|
+
|
33
|
+
expect(form.first_name).to eq "Tony"
|
34
|
+
expect(form.last_name).to eq "Stark"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "handles both symbols and strings as attribute keys" do
|
38
|
+
CustomForm = Class.new(Patterns::Form) do
|
39
|
+
attribute :first_name, String
|
40
|
+
attribute :last_name, String
|
41
|
+
attribute :email, String
|
42
|
+
attribute :age, Integer
|
43
|
+
end
|
44
|
+
resource = double(
|
45
|
+
attributes: {
|
46
|
+
"first_name" => "Bat",
|
47
|
+
last_name: "Man",
|
48
|
+
"email" => "bat@man.dev"
|
49
|
+
}
|
50
|
+
)
|
51
|
+
|
52
|
+
form = CustomForm.new(
|
53
|
+
resource, { first_name: "Christian", "last_name" => "Bale", "age" => 40 }
|
54
|
+
)
|
55
|
+
|
56
|
+
expect(form.first_name).to eq "Christian"
|
57
|
+
expect(form.last_name).to eq "Bale"
|
58
|
+
expect(form.email).to eq "bat@man.dev"
|
59
|
+
expect(form.age).to eq 40
|
60
|
+
end
|
61
|
+
|
62
|
+
context "if second parameter is ActionController::Parameters object" do
|
63
|
+
it "treats ActionController::Parameters as regular hash" do
|
64
|
+
CustomForm = Class.new(Patterns::Form) do
|
65
|
+
attribute :first_name, String
|
66
|
+
attribute :last_name, String
|
67
|
+
end
|
68
|
+
|
69
|
+
strong_parameters = ActionController::Parameters.new(
|
70
|
+
{ "first_name" => "Kobe", "last_name" => "Bryant" }
|
71
|
+
)
|
72
|
+
|
73
|
+
form = CustomForm.new(double, strong_parameters)
|
74
|
+
|
75
|
+
expect(form.first_name).to eq "Kobe"
|
76
|
+
expect(form.last_name).to eq "Bryant"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "if only parameter is ActionController::Parameters object" do
|
81
|
+
it "treats ActionController::Parameters as regular hash" do
|
82
|
+
CustomForm = Class.new(Patterns::Form) do
|
83
|
+
attribute :first_name, String
|
84
|
+
attribute :last_name, String
|
85
|
+
end
|
86
|
+
|
87
|
+
strong_parameters = ActionController::Parameters.new(
|
88
|
+
{ "first_name" => "Saul", "last_name" => "Goodman" }
|
89
|
+
)
|
90
|
+
|
91
|
+
form = CustomForm.new(strong_parameters)
|
92
|
+
|
93
|
+
expect(form.first_name).to eq "Saul"
|
94
|
+
expect(form.last_name).to eq "Goodman"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can be initialized without providing resource" do
|
99
|
+
CustomForm = Class.new(Patterns::Form)
|
100
|
+
|
101
|
+
form = CustomForm.new
|
102
|
+
|
103
|
+
expect(form).to be_a_kind_of(CustomForm)
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when resource exists" do
|
107
|
+
context "when resource responds to #attributes" do
|
108
|
+
it "assigns merged attributes from resource and passed as argument" do
|
109
|
+
CustomForm = Class.new(Patterns::Form) do
|
110
|
+
attribute :first_name, String
|
111
|
+
attribute :last_name, String
|
112
|
+
end
|
113
|
+
resource = double(attributes: { first_name: "Jack", last_name: "Black" })
|
114
|
+
|
115
|
+
form = CustomForm.new(resource, { first_name: "Tony" })
|
116
|
+
|
117
|
+
expect(form.first_name).to eq "Tony"
|
118
|
+
expect(form.last_name).to eq "Black"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "attempts to use public getters to populate missing attributes" do
|
122
|
+
CustomForm = Class.new(Patterns::Form) do
|
123
|
+
attribute :first_name, String
|
124
|
+
attribute :last_name, String
|
125
|
+
attribute :age, Integer
|
126
|
+
attribute :email, String
|
127
|
+
end
|
128
|
+
resource = double(attributes: { first_name: "Jack", last_name: "Black" }, age: 27)
|
129
|
+
|
130
|
+
form = CustomForm.new(resource, { first_name: "Tony" })
|
131
|
+
|
132
|
+
expect(form.first_name).to eq "Tony"
|
133
|
+
expect(form.last_name).to eq "Black"
|
134
|
+
expect(form.age).to eq 27
|
135
|
+
expect(form.email).to eq nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when resource does not respond to #attributes" do
|
140
|
+
it "assigns attributes passed as arguments" do
|
141
|
+
CustomForm = Class.new(Patterns::Form) do
|
142
|
+
attribute :first_name, String
|
143
|
+
attribute :last_name, String
|
144
|
+
end
|
145
|
+
|
146
|
+
form = CustomForm.new(double, { first_name: "Tony" })
|
147
|
+
|
148
|
+
expect(form.first_name).to eq "Tony"
|
149
|
+
expect(form.last_name).to eq nil
|
150
|
+
end
|
151
|
+
|
152
|
+
it "attempts to use public getters to populate missing attributes" do
|
153
|
+
CustomForm = Class.new(Patterns::Form) do
|
154
|
+
attribute :first_name, String
|
155
|
+
attribute :last_name, String
|
156
|
+
attribute :age, Integer
|
157
|
+
attribute :email, String
|
158
|
+
end
|
159
|
+
resource = double(last_name: "Black", age: 27)
|
160
|
+
|
161
|
+
form = CustomForm.new(resource, { first_name: "Tony" })
|
162
|
+
|
163
|
+
expect(form.first_name).to eq "Tony"
|
164
|
+
expect(form.last_name).to eq "Black"
|
165
|
+
expect(form.age).to eq 27
|
166
|
+
expect(form.email).to eq nil
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#save" do
|
173
|
+
context "when form is valid" do
|
174
|
+
it "requires #persist method to be implemented" do
|
175
|
+
CustomForm = Class.new(Patterns::Form)
|
176
|
+
|
177
|
+
form = CustomForm.new(double)
|
178
|
+
|
179
|
+
expect { form.save }.to raise_error NotImplementedError, "#persist has to be implemented"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns result of #persist method" do
|
183
|
+
CustomForm = Class.new(Patterns::Form) do
|
184
|
+
private
|
185
|
+
|
186
|
+
def persist
|
187
|
+
10
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
form = CustomForm.new(double)
|
192
|
+
result = form.save
|
193
|
+
|
194
|
+
expect(result).to eq 10
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "when form is invalid" do
|
199
|
+
it "does not call #persist method" do
|
200
|
+
CustomForm = Class.new(Patterns::Form) do
|
201
|
+
private
|
202
|
+
|
203
|
+
def persist
|
204
|
+
raise StandardError, "Should not be raised!"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
form = CustomForm.new(double)
|
208
|
+
allow(form).to receive(:valid?) { false }
|
209
|
+
|
210
|
+
expect { form.save }.to_not raise_error
|
211
|
+
end
|
212
|
+
|
213
|
+
it "returns false" do
|
214
|
+
CustomForm = Class.new(Patterns::Form) do
|
215
|
+
private
|
216
|
+
|
217
|
+
def persist
|
218
|
+
10
|
219
|
+
end
|
220
|
+
end
|
221
|
+
form = CustomForm.new(double)
|
222
|
+
allow(form).to receive(:valid?) { false }
|
223
|
+
|
224
|
+
expect(form.save).to eq false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#save!" do
|
230
|
+
context "#save returned falsey value" do
|
231
|
+
it "returns Pattern::Form::Invalid exception" do
|
232
|
+
CustomForm = Class.new(Patterns::Form) do
|
233
|
+
private
|
234
|
+
|
235
|
+
def persist
|
236
|
+
10
|
237
|
+
end
|
238
|
+
end
|
239
|
+
form = CustomForm.new(double)
|
240
|
+
allow(form).to receive(:save) { false }
|
241
|
+
|
242
|
+
expect { form.save! }.to raise_error Patterns::Form::Invalid
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "#save returned truthy value" do
|
247
|
+
it "returns value returned from #save" do
|
248
|
+
CustomForm = Class.new(Patterns::Form) do
|
249
|
+
private
|
250
|
+
|
251
|
+
def persist
|
252
|
+
10
|
253
|
+
end
|
254
|
+
end
|
255
|
+
form = CustomForm.new(double)
|
256
|
+
|
257
|
+
expect(form.save!).to eq 10
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe "#as" do
|
263
|
+
it "saves argument in @form_owner" do
|
264
|
+
CustomForm = Class.new(Patterns::Form)
|
265
|
+
form_owner = double("Form owner")
|
266
|
+
|
267
|
+
form = CustomForm.new(double).as(form_owner)
|
268
|
+
|
269
|
+
expect(form.instance_variable_get("@form_owner")).to eq form_owner
|
270
|
+
end
|
271
|
+
|
272
|
+
it "returns itself" do
|
273
|
+
CustomForm = Class.new(Patterns::Form)
|
274
|
+
|
275
|
+
form = CustomForm.new(double)
|
276
|
+
result = form.as(double)
|
277
|
+
|
278
|
+
expect(result).to eq form
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "#persisted?" do
|
283
|
+
context "when resource is nil" do
|
284
|
+
it "returns false" do
|
285
|
+
CustomForm = Class.new(Patterns::Form)
|
286
|
+
|
287
|
+
form = CustomForm.new
|
288
|
+
|
289
|
+
expect(form.persisted?).to eq false
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "when resource is not nil" do
|
294
|
+
context "when resource responds to #persisted?" do
|
295
|
+
it "returns resource#persisted?" do
|
296
|
+
CustomForm = Class.new(Patterns::Form)
|
297
|
+
|
298
|
+
form_1 = CustomForm.new(double(persisted?: true))
|
299
|
+
form_2 = CustomForm.new(double(persisted?: false))
|
300
|
+
|
301
|
+
expect(form_1.persisted?).to eq true
|
302
|
+
expect(form_2.persisted?).to eq false
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "when resource does not respond to #persisted?" do
|
307
|
+
it "returns false" do
|
308
|
+
CustomForm = Class.new(Patterns::Form)
|
309
|
+
|
310
|
+
form = CustomForm.new(double)
|
311
|
+
|
312
|
+
expect(form.persisted?).to eq false
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "#to_model" do
|
319
|
+
it "retruns itself" do
|
320
|
+
CustomForm = Class.new(Patterns::Form)
|
321
|
+
|
322
|
+
form = CustomForm.new(double)
|
323
|
+
|
324
|
+
expect(form.to_model).to eq form
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "#to_partial_path" do
|
329
|
+
it "returns nil" do
|
330
|
+
CustomForm = Class.new(Patterns::Form)
|
331
|
+
|
332
|
+
form = CustomForm.new(double)
|
333
|
+
|
334
|
+
expect(form.to_partial_path).to eq nil
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe "#to_key" do
|
339
|
+
it "returns nil" do
|
340
|
+
CustomForm = Class.new(Patterns::Form)
|
341
|
+
|
342
|
+
form = CustomForm.new(double)
|
343
|
+
|
344
|
+
expect(form.to_key).to eq nil
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "#model_name" do
|
349
|
+
describe "#param_key" do
|
350
|
+
context "resource exists" do
|
351
|
+
context "resource responds to #model_name" do
|
352
|
+
context "param_key is not defined" do
|
353
|
+
it "returns object responding to #param_key returning resource#param_key" do
|
354
|
+
CustomForm = Class.new(Patterns::Form)
|
355
|
+
resource = double(model_name: double(param_key: "resource_key"))
|
356
|
+
|
357
|
+
form = CustomForm.new(resource)
|
358
|
+
result = form.model_name
|
359
|
+
|
360
|
+
expect(result).to respond_to(:param_key)
|
361
|
+
expect(result.param_key).to eq "resource_key"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
context "param_key is defined" do
|
366
|
+
it "returns param_key" do
|
367
|
+
CustomForm = Class.new(Patterns::Form) do
|
368
|
+
param_key "test_key"
|
369
|
+
end
|
370
|
+
resource = double(model_name: double(param_key: "resource_key"))
|
371
|
+
|
372
|
+
form = CustomForm.new(resource)
|
373
|
+
result = form.model_name
|
374
|
+
|
375
|
+
expect(result.param_key).to eq "test_key"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
context "resource does not respond to #model_name" do
|
381
|
+
context "param_key is not defined" do
|
382
|
+
it "raises NoParamKey" do
|
383
|
+
CustomForm = Class.new(Patterns::Form)
|
384
|
+
|
385
|
+
form = CustomForm.new(double)
|
386
|
+
|
387
|
+
expect { form.model_name }.to raise_error(Patterns::Form::NoParamKey)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context "param_key is defined" do
|
392
|
+
it "returns param_key" do
|
393
|
+
CustomForm = Class.new(Patterns::Form) do
|
394
|
+
param_key "test_key"
|
395
|
+
end
|
396
|
+
|
397
|
+
form = CustomForm.new(double)
|
398
|
+
result = form.model_name
|
399
|
+
|
400
|
+
expect(result.param_key).to eq "test_key"
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context "resource does not exist" do
|
407
|
+
context "param_key is not defined" do
|
408
|
+
it "raises NoParamKey" do
|
409
|
+
CustomForm = Class.new(Patterns::Form)
|
410
|
+
|
411
|
+
form = CustomForm.new
|
412
|
+
|
413
|
+
expect { form.model_name }.to raise_error(Patterns::Form::NoParamKey)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
context "param_key is defined" do
|
418
|
+
it "returns param_key" do
|
419
|
+
CustomForm = Class.new(Patterns::Form) do
|
420
|
+
param_key "test_key"
|
421
|
+
end
|
422
|
+
|
423
|
+
form = CustomForm.new
|
424
|
+
result = form.model_name
|
425
|
+
|
426
|
+
expect(result.param_key).to eq "test_key"
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-patterns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stevo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 4.2.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: actionpack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.2.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.2.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: virtus
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rspec
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,11 +112,13 @@ files:
|
|
84
112
|
- VERSION
|
85
113
|
- lib/patterns.rb
|
86
114
|
- lib/patterns/collection.rb
|
115
|
+
- lib/patterns/form.rb
|
87
116
|
- lib/patterns/query.rb
|
88
117
|
- lib/patterns/service.rb
|
89
118
|
- lib/rails-patterns.rb
|
90
119
|
- rails-patterns.gemspec
|
91
120
|
- spec/patterns/collection_spec.rb
|
121
|
+
- spec/patterns/form_spec.rb
|
92
122
|
- spec/patterns/query_spec.rb
|
93
123
|
- spec/patterns/service_spec.rb
|
94
124
|
- spec/spec_helper.rb
|