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 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