mini_form 0.1.0 → 0.2.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: 6d427b889425b8c2acd15827c88bc61430a70652
4
- data.tar.gz: 582b6cf7164ab75b6f4df2f306eeb5580d9a0f3c
3
+ metadata.gz: 6d0c0c470b74ebc5e81006fa9f4f226de8d821d6
4
+ data.tar.gz: 38393dfb6c142a1f85dca47a2aa16072a63f657f
5
5
  SHA512:
6
- metadata.gz: 9e0f762fd07558b4c30386d9cd2a9a51b8855efb0279bb70b7f842d0e3748d7a413a0f449a31eb1d448e8b93c649930b3b90dbbb9ece8a4151ae013dd0f4d096
7
- data.tar.gz: d23461cd996d47cbbdfe7efd5219aed986a72e83c5693e6d1e413927fa947786c200bd1e6bb1cd227573af8f4b1a8c9be7d89926111b4fc3bbd8fbd5efeec448
6
+ metadata.gz: 1f8c4900a91ceedd1b9f7c12b73e5b2fc30f033fd47783ae792a4c9e8dbb3f0bae93c65953da6ddebf1bbc75e3bb8c4484e863e33925b67672f11246e4b094b6
7
+ data.tar.gz: 5a3a73ad3547ba949a1900918a1cdcfefc73c8bf77d0764d89fa0858c6fe8ae075a0493066f80b65d62429160e5a93d2734303d75fcc66d8b8746596d3455730
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 0.2.0
4
+
5
+ * Don't expose model name on `model`. _(security fix)_
6
+
7
+ * Included `ActiveModel::Validations::Callbacks` to `MiniForm::Model`
8
+
9
+ * Added read option to `model`:
10
+
11
+ ```ruby
12
+ class EditProfile
13
+ include MiniForm::Model
14
+
15
+ model :user, attributes: %i(email name), read:%(id)
16
+ end
17
+
18
+ profile = EditProfile.new(user: user)
19
+ profile.id
20
+ profile.id = 1 # raises NoMethodError
21
+ ```
22
+
23
+
24
+ * MiniForm::Model can be inherited
25
+ * Added `assigment` callbacks, called when attributes are assigned
26
+ * Added `assign_attributes` alias to `attributes=`
27
+ * Exposed `errors` on MiniForm::InvalidForm
28
+ * Added descriptive message MiniForm::InvalidForm
29
+ * Made `.attribute_names` public
30
+
3
31
  ## Version 0.1.0
4
32
 
5
33
  * Initial release
data/README.md CHANGED
@@ -46,7 +46,7 @@ class ProductsController < ApplicationController
46
46
  @product = ProductForm.new
47
47
 
48
48
  if @product.update(product_params)
49
- redirect_to product_path(product.id)
49
+ redirect_to product_path(@product.id)
50
50
  else
51
51
  render :edit
52
52
  end
@@ -130,7 +130,7 @@ Combines delegated attributes and nested validation into a single call.
130
130
  class SignUpForm
131
131
  include MiniForm::Model
132
132
 
133
- modal :user, attributes: %i(name email)
133
+ model :user, attributes: %i(name email)
134
134
  model :account, attributes: %i(company_name plan)
135
135
 
136
136
  def initialize
@@ -147,13 +147,13 @@ end
147
147
 
148
148
  ### Auto saving nested models
149
149
 
150
- In most of the time `perform` is just calling `save!`.
150
+ Most of the time `perform` is just calling `save!`. We can avoid this by using `model`'s `save` option.
151
151
 
152
152
  ```ruby
153
153
  class SignUpForm
154
154
  include MiniForm::Model
155
155
 
156
- modal :user, attributes: %i(name email), save: true
156
+ model :user, attributes: %i(name email), save: true
157
157
  model :account, attributes: %i(company_name plan), save: true
158
158
 
159
159
  def initialize
@@ -194,6 +194,27 @@ class SignUpForm
194
194
  end
195
195
  ```
196
196
 
197
+ ### Delegating model attributes
198
+
199
+ ```ruby
200
+ class SignUpForm
201
+ include MiniForm::Model
202
+
203
+ model :user, attributes: %i(name email), read: %i(id)
204
+
205
+ def initialize
206
+ @user = User.new account: @account
207
+ end
208
+ end
209
+ ```
210
+
211
+ ```
212
+ form = SignUpForm.new
213
+ form.update! form_params
214
+ form.id # => delegates to `user.id`
215
+ form.id = 42 # => raises `NoMethodError`
216
+ ```
217
+
197
218
  ### Methods
198
219
 
199
220
  <table>
@@ -209,6 +230,10 @@ end
209
230
  <td>.attributes</td>
210
231
  <td>Defines an attribute, it can delegate to sub object</td>
211
232
  </tr>
233
+ <tr>
234
+ <td>.attribute_names</td>
235
+ <td>Returns list of attribute names</td>
236
+ </tr>
212
237
  <tr>
213
238
  <td>#initialize</td>
214
239
  <td>Meant to be overwritten. By defaults calls `attributes=`</td>
@@ -241,6 +266,14 @@ end
241
266
  <td>#after_update</td>
242
267
  <td>Meant to be overwritten.</td>
243
268
  </tr>
269
+ <tr>
270
+ <td>#before_assigment</td>
271
+ <td>Meant to be overwritten.</td>
272
+ </tr>
273
+ <tr>
274
+ <td>#after_assigment</td>
275
+ <td>Meant to be overwritten.</td>
276
+ </tr>
244
277
  <tr>
245
278
  <td>#transaction</td>
246
279
  <td>If ActiveRecord is available, wraps `perform` in transaction.</td>
@@ -1,4 +1,11 @@
1
1
  module MiniForm
2
2
  class InvalidForm < StandardError
3
+ attr_reader :errors
4
+
5
+ def initialize(object)
6
+ @errors = object.errors
7
+
8
+ super "Form validation failed for: #{errors.keys.join(', ')}"
9
+ end
3
10
  end
4
11
  end
@@ -3,9 +3,10 @@ require 'active_model'
3
3
 
4
4
  module MiniForm
5
5
  module Model
6
- def self.included(base)
6
+ def self.included(base) # rubocop:disable MethodLength
7
7
  base.class_eval do
8
8
  include ActiveModel::Validations
9
+ include ActiveModel::Validations::Callbacks
9
10
  include ActiveModel::Conversion
10
11
 
11
12
  extend ActiveModel::Naming
@@ -14,9 +15,13 @@ module MiniForm
14
15
  extend ClassMethods
15
16
 
16
17
  define_model_callbacks :update
18
+ define_model_callbacks :assigment
17
19
 
18
20
  before_update :before_update
19
21
  after_update :after_update
22
+
23
+ before_assigment :before_assigment
24
+ after_assigment :after_assigment
20
25
  end
21
26
  end
22
27
 
@@ -29,11 +34,15 @@ module MiniForm
29
34
  end
30
35
 
31
36
  def attributes=(attributes)
32
- attributes.slice(*self.class.attribute_names).each do |name, value|
33
- public_send "#{name}=", value
37
+ run_callbacks :assigment do
38
+ attributes.slice(*self.class.attribute_names).each do |name, value|
39
+ public_send "#{name}=", value
40
+ end
34
41
  end
35
42
  end
36
43
 
44
+ alias_method :assign_attributes, :attributes=
45
+
37
46
  def attributes
38
47
  Hash[self.class.attribute_names.map { |name| [name, public_send(name)] }]
39
48
  end
@@ -54,7 +63,7 @@ module MiniForm
54
63
  end
55
64
 
56
65
  def update!(attributes = {})
57
- fail InvalidForm unless update attributes
66
+ fail InvalidForm, self unless update attributes
58
67
  self
59
68
  end
60
69
 
@@ -85,8 +94,25 @@ module MiniForm
85
94
  # noop
86
95
  end
87
96
 
97
+ def before_assigment
98
+ # noop
99
+ end
100
+
101
+ def after_assigment
102
+ # noop
103
+ end
104
+
88
105
  module ClassMethods
89
- # :api: private
106
+ def inherited(base)
107
+ new_attribute_names = attribute_names.dup
108
+ new_models_to_save = models_to_save.dup
109
+
110
+ base.instance_eval do
111
+ @attribute_names = new_attribute_names
112
+ @models_to_save = new_models_to_save
113
+ end
114
+ end
115
+
90
116
  def attribute_names
91
117
  @attribute_names ||= []
92
118
  end
@@ -102,15 +128,18 @@ module MiniForm
102
128
  if delegate.nil?
103
129
  attr_accessor(*attributes)
104
130
  else
105
- self.delegate(*attributes, to: delegate, prefix: prefix, allow_nil: allow_nil)
106
- self.delegate(*attributes.map { |attr| "#{attr}=" }, to: delegate, prefix: prefix, allow_nil: allow_nil)
131
+ delegate(*attributes, to: delegate, prefix: prefix, allow_nil: allow_nil)
132
+ delegate(*attributes.map { |attr| "#{attr}=" }, to: delegate, prefix: prefix, allow_nil: allow_nil)
107
133
  end
108
134
  end
109
135
 
110
- def model(name, attributes: [], prefix: nil, allow_nil: nil, save: false)
111
- attributes(name)
136
+ def model(name, attributes: [], read: [], prefix: nil, allow_nil: nil, save: false) # rubocop:disable ParameterLists
137
+ attr_accessor name
138
+
112
139
  attributes(*attributes, delegate: name, prefix: prefix, allow_nil: allow_nil) unless attributes.empty?
113
140
 
141
+ delegate(*read, to: name, prefix: prefix, allow_nil: nil)
142
+
114
143
  validates name, 'mini_form/nested' => true
115
144
 
116
145
  models_to_save << name if save
@@ -1,3 +1,3 @@
1
1
  module MiniForm
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -2,18 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  module MiniForm
4
4
  describe Model do
5
- let(:user) { User.new name: 'Name' }
5
+ let(:user) { User.new id: 1, name: 'name', age: 28 }
6
6
 
7
7
  class User
8
8
  include ActiveModel::Model
9
9
 
10
- attr_accessor :name
10
+ attr_accessor :id, :name, :age
11
11
 
12
12
  validates :name, presence: true
13
13
  end
14
14
 
15
15
  Example = Class.new do
16
16
  include Model
17
+
17
18
  attributes :name, :price
18
19
  end
19
20
 
@@ -29,12 +30,6 @@ module MiniForm
29
30
  end
30
31
  end
31
32
 
32
- ExampleWithModel = Class.new do
33
- include Model
34
-
35
- model :user, attributes: %i(name)
36
- end
37
-
38
33
  describe 'acts as ActiveModel' do
39
34
  include ActiveModel::Lint::Tests
40
35
 
@@ -59,6 +54,23 @@ module MiniForm
59
54
  end
60
55
  end
61
56
 
57
+ describe 'inheritance' do
58
+ it 'can be inherited' do
59
+ parent_class = Class.new do
60
+ include Model
61
+
62
+ attributes :name
63
+ end
64
+
65
+ child_class = Class.new(parent_class) do
66
+ attributes :age
67
+ end
68
+
69
+ expect(parent_class.attribute_names).to eq %i(name)
70
+ expect(child_class.attribute_names).to eq %i(name age)
71
+ end
72
+ end
73
+
62
74
  describe '.attributes' do
63
75
  it 'generates getters' do
64
76
  object = Example.new name: 'value'
@@ -88,19 +100,36 @@ module MiniForm
88
100
  end
89
101
 
90
102
  describe '.model' do
103
+ ExampleWithModel = Class.new do
104
+ include Model
105
+
106
+ model :user, attributes: %i(name), read: %i(id)
107
+
108
+ def initialize(user)
109
+ self.user = user
110
+ end
111
+ end
112
+
91
113
  it 'generates model accessors' do
92
- object = ExampleWithModel.new user: user
114
+ object = ExampleWithModel.new user
93
115
  expect(object.user).to eq user
94
116
  end
95
117
 
118
+ it 'can delegate only a reader' do
119
+ object = ExampleWithModel.new user
120
+
121
+ expect(object).not_to respond_to :id=
122
+ expect(object.id).to eq user.id
123
+ end
124
+
96
125
  it 'can delegate model attributes' do
97
- object = ExampleWithModel.new user: user
126
+ object = ExampleWithModel.new user
98
127
  expect(object.name).to eq user.name
99
128
  end
100
129
 
101
130
  it 'performs nested validation for model' do
102
131
  user = User.new
103
- object = ExampleWithModel.new user: user
132
+ object = ExampleWithModel.new user
104
133
 
105
134
  expect(object).not_to be_valid
106
135
  expect(object.errors[:name]).to be_present
@@ -143,6 +172,24 @@ module MiniForm
143
172
  end
144
173
  end
145
174
 
175
+ ['attributes=', 'assign_attributes'].each do |method_name|
176
+ describe "##{method_name}" do
177
+ it 'sets attributes' do
178
+ object = Example.new
179
+ object.public_send method_name, name: 'iPhone', price: '$5'
180
+
181
+ expect(object.attributes).to eq name: 'iPhone', price: '$5'
182
+ end
183
+
184
+ it 'ignores not listed attributes' do
185
+ object = Example.new
186
+ object.public_send method_name, invalid: 'value'
187
+
188
+ expect(object.attributes).to eq name: nil, price: nil
189
+ end
190
+ end
191
+ end
192
+
146
193
  describe '#update' do
147
194
  ExampleForUpdate = Class.new do
148
195
  include Model
@@ -156,6 +203,10 @@ module MiniForm
156
203
  include Model
157
204
 
158
205
  model :user, attributes: %i(name), save: true
206
+
207
+ def initialize(user: user)
208
+ self.user = user
209
+ end
159
210
  end
160
211
 
161
212
  it 'updates attributes' do
@@ -197,6 +248,15 @@ module MiniForm
197
248
  object.update
198
249
  end
199
250
 
251
+ it 'supports assign callbacks' do
252
+ object = ExampleForUpdate.new
253
+
254
+ expect(object).to receive(:before_assigment)
255
+ expect(object).to receive(:after_assigment)
256
+
257
+ object.update name: 'value'
258
+ end
259
+
200
260
  it 'returns false when validations fail' do
201
261
  object = ExampleForUpdate.new name: nil
202
262
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radoslav Stankov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-02 00:00:00.000000000 Z
11
+ date: 2015-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel