reform 0.2.4 → 0.2.5

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: 201a01642c1743227a0209c716240374d884189f
4
- data.tar.gz: 4327aa39fbfae302b0078c6787a0df387aa08080
3
+ metadata.gz: 76401e86f77b8b06874c24e8257dc97e5d0e66c0
4
+ data.tar.gz: 37e55343b9fc0d9b7d7764c4988f01f22db0d4fc
5
5
  SHA512:
6
- metadata.gz: 42bad26c32be288977aae6cc6be0f634382b5bdc0cc733ce5b0f2084096db897df33b7126923b58f9d3eee4f50e63b1ee0095650a2c414c12a2c1e9fbc5148fd
7
- data.tar.gz: 94ebe84171e96c49fff4adbdc704d5c94cebc0713a27a35b86941ffc33e23947e7371afe9b8d6fce3f7bffee1cd6ba2363418635abd87f65230bbebf7b2bf385
6
+ metadata.gz: 9b7b1d9f9ed49bff28ac3bd83802b8d73d20005bd9126a1d25b6a342e031a0e44a5ed675686242468c29c2ac7bf7c5b71bb7bf40c53ec5811d26ba601c02ca97
7
+ data.tar.gz: 557e270fa0dfbaad214790f3aaa0bf579d90feeb4b70a5193fcd2bb526b4ada902d3308081ae67b0a6fdcd13b2048afd389b9625cf7c9a8819668a3c1f247095
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.2.5
2
+
3
+ * Allow proper form inheritance. When having `HitForm < SongForm < Reform::Form` the `HitForm` class will contain `SongForm`'s properties in addition to its own fields.
4
+ * `::model` is now inherited properly.
5
+
1
6
  ## 0.2.4
2
7
 
3
8
  * Accessors for properties (e.g. `title` and `title=`) can now be overridden in the form *and* call `super`. This is extremely helpful if you wanna do "manual coercion" since the accessors are invoked in `#validate`. Thanks to @cj for requesting this.
data/README.md CHANGED
@@ -271,12 +271,17 @@ class SongWithLabelForm < Reform::Form
271
271
  property :title, on: :song
272
272
  property :city, on: :label
273
273
 
274
+ model :song # only needed in ActiveModel context.
275
+
274
276
  validates :title, :city, presence: true
275
277
  end
276
278
  ```
277
279
 
278
280
  Note that reform needs to know about the owner objects of properties. You can do so by using the `on:` option.
279
281
 
282
+ Also, the form needs to have a main object configured. This is where ActiveModel-methods like `#persisted?` or '#id' are delegated to. Use `::model` to define the main object.
283
+
284
+
280
285
  ### Composition: Setup
281
286
 
282
287
  The constructor slightly differs.
@@ -415,6 +420,15 @@ Reform provides the following `ActiveRecord` specific features. They're mixed in
415
420
  * Uniqueness validations. Use `validates_uniqueness_of` in your form.
416
421
  * Calling `Form#save` will explicitely call `save` on your model (added in 0.2.1) which will usually trigger a database insertion or update.
417
422
 
423
+ As mentioned in the [Rails Integration](https://github.com/apotonick/reform#rails-integration) section some Rails 4 setups do not properly load.
424
+
425
+ You may want to include the module manually then.
426
+
427
+ ```ruby
428
+ class SongForm < Reform::Form
429
+ include Reform::Form::ActiveRecord
430
+ ```
431
+
418
432
 
419
433
  ## ActiveModel Compliance
420
434
 
Binary file
@@ -4,15 +4,18 @@ require 'ostruct'
4
4
  require 'reform/composition'
5
5
  require 'reform/representer'
6
6
 
7
+ require 'uber/inheritable_attr'
8
+
9
+
7
10
  module Reform
8
11
  class Form
9
12
  extend Forwardable
10
- # reasons for delegation:
11
- # presentation: this object is used in the presentation layer by #form_for.
12
- # problem: #form_for uses respond_to?(:email_before_type_cast) which goes to an internal hash in the actual record.
13
- # validation: this object also contains the validation rules itself, should be separated.
14
13
 
15
- # Allows using property and friends in the Form itself. Forwarded to the internal representer_class.
14
+ extend Uber::InheritableAttr
15
+ inheritable_attr :representer_class
16
+ self.representer_class = Class.new(Reform::Representer)
17
+
18
+
16
19
  module PropertyMethods
17
20
  extend Forwardable
18
21
 
@@ -43,10 +46,6 @@ module Reform
43
46
  definition.options[:instance] = true # just to make typed? work
44
47
  end
45
48
 
46
- def representer_class
47
- @representer_class ||= Class.new(Reform::Representer)
48
- end
49
-
50
49
  private
51
50
  def create_accessor(name)
52
51
  # Make a module that contains these very accessors, then include it
@@ -55,18 +55,28 @@ module Reform::Form::ActiveModel
55
55
  end
56
56
  end
57
57
 
58
+
58
59
  module ClassMethods
60
+ # this module is only meant to extend (not include). # DISCUSS: is this a sustainable concept?
61
+ def self.extended(base)
62
+ base.class_eval do
63
+ extend Hooks::InheritableAttribute
64
+ inheritable_attr :model_options
65
+ end
66
+ end
67
+
68
+
59
69
  # Set a model name for this form if the infered is wrong.
60
70
  #
61
71
  # class CoverSongForm < Reform::Form
62
72
  # model :song
63
73
  def model(main_model, options={})
64
- @model_options = [main_model, options] # FIXME: make inheritable!
74
+ self.model_options = [main_model, options]
65
75
  end
66
76
 
67
77
  def model_name
68
- if @model_options
69
- form_name = @model_options.first.to_s.camelize
78
+ if model_options
79
+ form_name = model_options.first.to_s.camelize
70
80
  else
71
81
  form_name = name.sub(/Form$/, "")
72
82
  end
@@ -5,12 +5,13 @@ class Reform::Form
5
5
  module Composition
6
6
  def self.included(base)
7
7
  base.class_eval do
8
+ extend Reform::Form::ActiveModel::ClassMethods # ::model.
8
9
  extend ClassMethods
9
10
  end
10
11
  end
11
12
 
12
13
  module ClassMethods
13
- include Reform::Form::ActiveModel::ClassMethods # ::model.
14
+ #include Reform::Form::ActiveModel::ClassMethods # ::model.
14
15
 
15
16
  def model_class # DISCUSS: needed?
16
17
  rpr = representer_class
@@ -47,6 +47,10 @@ module Reform
47
47
  each(&block)
48
48
  end
49
49
 
50
+ def self.clone # called in inheritable_attr :representer_class.
51
+ Class.new(self) # By subclassing, representable_attrs.clone is called.
52
+ end
53
+
50
54
  private
51
55
  def clone_config!
52
56
  # TODO: representable_attrs.clone! which does exactly what's done below.
@@ -1,3 +1,3 @@
1
1
  module Reform
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "representable", ">= 1.7.5"
22
+ spec.add_dependency "uber", "~> 0.0.4"
22
23
  spec.add_dependency "activemodel"
23
24
  spec.add_development_dependency "bundler", "~> 1.3"
24
25
  spec.add_development_dependency "rake", ">= 10.1.0"
25
- spec.add_development_dependency "minitest"
26
+ spec.add_development_dependency "minitest", "4.2.0"
26
27
  spec.add_development_dependency "activerecord"
27
28
  spec.add_development_dependency "sqlite3"
28
29
  spec.add_development_dependency "virtus"
@@ -30,6 +30,15 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
30
30
 
31
31
  it { class_with_model.model_name.must_be_kind_of ActiveModel::Name }
32
32
  it { class_with_model.model_name.to_s.must_equal "Album" }
33
+
34
+
35
+ let (:subclass_of_class_with_model) {
36
+ Class.new(class_with_model)
37
+ }
38
+
39
+ it { subclass_of_class_with_model.model_name.must_be_kind_of ActiveModel::Name }
40
+ it { subclass_of_class_with_model.model_name.to_s.must_equal 'Album' }
41
+
33
42
 
34
43
  describe "inline with model" do
35
44
  let (:form_class) {
@@ -198,6 +207,18 @@ class ActiveModelWithCompositionTest < MiniTest::Spec
198
207
  model :song
199
208
  end.new(:song => rio, :artist => duran).song.must_equal rio
200
209
  end
210
+
211
+ # it "delegates when you call ::model" do
212
+ # class SongOnlyForm < Reform::Form
213
+ # include Composition
214
+ # include Reform::Form::ActiveModel
215
+
216
+ # property :title, :on => :song
217
+ # model :song
218
+
219
+ # self
220
+ # end.new(:song => rio, :artist => duran).persisted?
221
+ # end
201
222
  end
202
223
 
203
224
 
@@ -240,4 +261,4 @@ class ActiveModelWithCompositionTest < MiniTest::Spec
240
261
 
241
262
  AnotherForm.new(:song => rio).song.must_equal rio
242
263
  end
243
- end
264
+ end
@@ -112,12 +112,14 @@ class ReformTest < ReformSpec
112
112
  end
113
113
 
114
114
  it "exposes input via property accessors" do
115
- comp.name.must_equal nil
116
- form.name.must_equal nil
117
-
118
115
  form.validate("name" => "Duran Duran")
119
116
 
120
117
  form.name.must_equal "Duran Duran"
118
+ end
119
+
120
+ it "doesn't change model properties" do
121
+ form.validate("name" => "Duran Duran")
122
+
121
123
  comp.name.must_equal nil # don't touch model, yet.
122
124
  end
123
125
 
@@ -213,6 +215,22 @@ class ReformTest < ReformSpec
213
215
  describe "#model" do
214
216
  it { form.model.must_equal comp }
215
217
  end
218
+
219
+
220
+ describe "inheritance" do
221
+ class HitForm < SongForm
222
+ property :position
223
+ validates :position, :presence => true
224
+ end
225
+
226
+ let (:form) { HitForm.new(OpenStruct.new) }
227
+ it do
228
+ form.validate({"title" => "The Body"})
229
+ form.title.must_equal "The Body"
230
+ form.position.must_equal nil
231
+ form.errors.messages.must_equal({:name=>["can't be blank"], :position=>["can't be blank"]})
232
+ end
233
+ end
216
234
  end
217
235
 
218
236
  class EmptyAttributesTest < MiniTest::Spec
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-22 00:00:00.000000000 Z
12
+ date: 2014-03-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: representable
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - '>='
26
26
  - !ruby/object:Gem::Version
27
27
  version: 1.7.5
28
+ - !ruby/object:Gem::Dependency
29
+ name: uber
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 0.0.4
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: 0.0.4
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: activemodel
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -71,16 +85,16 @@ dependencies:
71
85
  name: minitest
72
86
  requirement: !ruby/object:Gem::Requirement
73
87
  requirements:
74
- - - '>='
88
+ - - '='
75
89
  - !ruby/object:Gem::Version
76
- version: '0'
90
+ version: 4.2.0
77
91
  type: :development
78
92
  prerelease: false
79
93
  version_requirements: !ruby/object:Gem::Requirement
80
94
  requirements:
81
- - - '>='
95
+ - - '='
82
96
  - !ruby/object:Gem::Version
83
- version: '0'
97
+ version: 4.2.0
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: activerecord
86
100
  requirement: !ruby/object:Gem::Requirement
@@ -225,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
239
  version: '0'
226
240
  requirements: []
227
241
  rubyforge_project:
228
- rubygems_version: 2.0.3
242
+ rubygems_version: 2.0.2
229
243
  signing_key:
230
244
  specification_version: 4
231
245
  summary: Decouples your models from form by giving you form objects with validation,