rails-patterns 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2318f3f2a2753ab5159fa535d0245110eca7133c
4
- data.tar.gz: 6031c700f903e0a62ef3a9951f83932d590a887c
3
+ metadata.gz: af39ea7538e043642610c2ea2ff35a9fa5c8a0e7
4
+ data.tar.gz: ff3231a21b94a92280acf664497a40b3039f7140
5
5
  SHA512:
6
- metadata.gz: 4eabca7c85d89675918642a3f07c72effce0fccb4d709cc4d13c986cb549b9216dc7913cf2303ca464efa69c157b168f3d71301dd0422af13c738fce639e47e4
7
- data.tar.gz: b3d706d159d0abedab70f3aadb818c1b977463aa37e683f8a5bd2b3d139a187e3ac72d4b18b98def990ff739ce5d5b224b9de2f03c27e5585283f4213d4479df
6
+ metadata.gz: c3653e8ce337a6225ad14162600f8b2a462321fcca8595f8f1463920935749e17b48d3f7359530dff528bc7ff3f7175ddaa23b7ac1203903ff42b296bfb8c51d
7
+ data.tar.gz: 37f4e6cb0fc581140dfee9372386b9d37b77a092d63d798fb98d8617475ac249d2e8f49aa9d8092ae5630e3de5e48a0aa6677ed7d62e89429a788c9fb51e44f2
data/Gemfile CHANGED
@@ -1,7 +1,8 @@
1
1
  source "https://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- gem "activerecord", ">= 4.2.6"
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
- CustomerEventsCollection.for(customer)
170
- CustomerEventsCollection.for(customer, label_method: "name")
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.3.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
@@ -2,3 +2,4 @@ require "patterns"
2
2
  require "patterns/query"
3
3
  require "patterns/service"
4
4
  require "patterns/collection"
5
+ require "patterns/form"
@@ -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.3.0 ruby lib
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.3.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-19"
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.3.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-19 00:00:00.000000000 Z
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