super_form 0.0.1 → 0.1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/field.rb +15 -9
  3. data/lib/field/bare.rb +3 -0
  4. data/lib/field/base.rb +54 -15
  5. data/lib/field/cnpj.rb +5 -7
  6. data/lib/field/cpf.rb +6 -8
  7. data/lib/field/email.rb +4 -4
  8. data/lib/field/float.rb +7 -0
  9. data/lib/field/form.rb +30 -0
  10. data/lib/field/form_array.rb +32 -0
  11. data/lib/field/form_proxy.rb +16 -0
  12. data/lib/field/password.rb +6 -10
  13. data/lib/field/person_type.rb +0 -4
  14. data/lib/field/telephone.rb +2 -7
  15. data/lib/person_type.rb +2 -8
  16. data/lib/presenter.rb +90 -0
  17. data/lib/presenter/cnpj.rb +9 -0
  18. data/lib/presenter/cpf.rb +9 -0
  19. data/lib/presenter/form.rb +20 -0
  20. data/lib/presenter/password.rb +7 -0
  21. data/lib/presenter/person_type.rb +9 -0
  22. data/lib/presenter/telephone.rb +9 -0
  23. data/lib/super_form.rb +21 -156
  24. data/lib/super_form/fieldable.rb +78 -0
  25. data/lib/super_form/fieldset.rb +32 -0
  26. data/lib/super_form/version.rb +1 -1
  27. data/spec/lib/field/form_array.rb +71 -0
  28. data/spec/lib/field/form_array_spec.rb +71 -0
  29. data/spec/lib/field/form_spec.rb +68 -0
  30. data/spec/lib/field_spec.rb +13 -0
  31. data/spec/lib/person_type_spec.rb +66 -0
  32. data/spec/lib/presenter/cnpj_spec.rb +9 -0
  33. data/spec/lib/presenter/cpf_spec.rb +9 -0
  34. data/spec/lib/presenter/form_spec.rb +39 -0
  35. data/spec/lib/presenter/password_spec.rb +8 -0
  36. data/spec/lib/presenter/person_type_spec.rb +8 -0
  37. data/spec/lib/presenter/telephone_spec.rb +9 -0
  38. data/spec/lib/presenter_spec.rb +166 -0
  39. data/spec/lib/super_form/fieldable_spec.rb +82 -0
  40. data/spec/lib/super_form_spec.rb +120 -65
  41. data/spec/lib/telephone_spec.rb +32 -0
  42. data/spec/spec_helper.rb +4 -11
  43. data/spec/support/presenter_name.rb +28 -0
  44. data/super_form.gemspec +1 -0
  45. metadata +60 -2
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'super_form/fieldable'
3
+ require 'field/bare'
4
+
5
+ describe SuperForm::Fieldable do
6
+ context 'when no fieldset is defined' do
7
+ it 'returns no fieldsets' do
8
+ expect(class_with_no_fields.fieldsets).to eq({})
9
+ end
10
+ end
11
+
12
+ context 'when fields and fieldsets are defined' do
13
+ let(:dummy) { class_with_fields }
14
+
15
+ it 'has the right number of fields' do
16
+ expect(dummy.fields.length).to eq 5
17
+ end
18
+
19
+ it 'has field values which are the same as the form attributes' do
20
+ dummy.name = 'Thiago'
21
+ dummy.ball = "Let's have it"
22
+ dummy.car = "Let's drive it"
23
+ dummy.email = "findme@athome.com"
24
+ dummy.phone = '12345'
25
+
26
+ expect(dummy.field(:name).value).to eq dummy.name
27
+ expect(dummy.field(:ball).value).to eq dummy.ball
28
+ expect(dummy.field(:car).value).to eq dummy.car
29
+ expect(dummy.field(:email).value).to eq dummy.email
30
+ expect(dummy.field(:phone).value).to eq dummy.phone
31
+ end
32
+
33
+ it 'defines fieldsets which contain form fields' do
34
+ general = dummy.fieldset(:general)
35
+ expect(general.fields).to eq({
36
+ name: dummy.field(:name)
37
+ })
38
+
39
+ toys = dummy.fieldset(:toys)
40
+ expect(toys.fields).to eq ({
41
+ ball: dummy.field(:ball),
42
+ car: dummy.field(:car)
43
+ })
44
+ end
45
+
46
+ it 'uses :default fieldset for fields defined outside a fieldset' do
47
+ default = dummy.fieldset(:default)
48
+ expect(default.fields).to eq ({
49
+ email: dummy.field(:email),
50
+ phone: dummy.field(:phone)
51
+ })
52
+ end
53
+ end
54
+ end
55
+
56
+ def class_with_fields(&block)
57
+ Class.new do
58
+ include SuperForm::Fieldable
59
+
60
+ attr_accessor :name, :ball, :car, :email, :phone
61
+
62
+ fieldset :general do
63
+ field :name, Field::Text, presence: true
64
+ end
65
+
66
+ fieldset :toys do
67
+ field :ball, Field::Bare
68
+ field :car, Field::Text
69
+ end
70
+
71
+ field :email, Field::Text
72
+ field :phone, Field::Text, length: { minimum: 4 }
73
+
74
+ instance_eval(&block) if block_given?
75
+ end.new
76
+ end
77
+
78
+ def class_with_no_fields
79
+ Class.new do
80
+ include SuperForm::Fieldable
81
+ end
82
+ end
@@ -1,98 +1,153 @@
1
- require 'spec_helper'
2
1
  require 'super_form'
3
- require 'active_model'
2
+ require 'field/bare'
3
+ require 'spec_helper'
4
4
 
5
- module Field
6
- class Null < Base; end
7
- end
5
+ describe SuperForm do
6
+ it 'includes the Fieldable module' do
7
+ form = Class.new { include SuperForm }
8
+ expect(form.ancestors).to include SuperForm::Fieldable
9
+ end
8
10
 
9
- class DummyForm
10
- include SuperForm
11
- end
11
+ it 'runs the setup method when instantiated' do
12
+ form = Class.new do
13
+ include SuperForm
14
+ attr_reader :test_setup
12
15
 
13
- describe SuperForm do
14
- before(:all) do
15
- DummyForm.class_eval do
16
- fieldset :general do
17
- field :name, Field::Text, presence: true
16
+ setup { @test_setup = 'it worked' }
17
+ end
18
+
19
+ expect(form.new.test_setup).to eq('it worked')
20
+ end
21
+
22
+ it 'is never persisted' do
23
+ form = form_with_fields
24
+ form.name = 'Thiago'
25
+ form.save
26
+
27
+ expect(form.persisted?).to be_false
28
+ expect(form_with_no_fields.persisted?).to be_false
29
+ end
30
+
31
+ describe 'ActiveModel naming' do
32
+ context 'when the class name method returns nil' do
33
+ it 'relies on to_s to guess the model name' do
34
+ form = Class.new { include SuperForm }
35
+
36
+ expect(form).to receive(:name)
37
+ expect(form).to receive(:to_s)
38
+
39
+ form.model_name
18
40
  end
19
41
 
20
- fieldset :toys do
21
- field :ball, Field::Null
22
- field :car, Field::Text
42
+ it 'returns the correct model name' do
43
+ form = Class.new { include SuperForm }
44
+ form.stub(:to_s).and_return('InexistentForm')
45
+
46
+ expect(form.model_name.name).to eq 'Inexistent'
23
47
  end
24
48
  end
25
49
 
26
- @dummy_form = DummyForm.new
27
- end
50
+ context 'when the class name method returns the class name' do
51
+ it 'uses the name method to guess the model name' do
52
+ form = Class.new { include SuperForm }
53
+ form.stub(:name).and_return 'InexistentForm'
28
54
 
29
- context 'when a field is defined at class level' do
30
- before(:all) { @fields = DummyForm.fields }
31
-
32
- it 'stores the basic field objects at class level' do
33
- expect(@fields.fetch(:name)).to be_instance_of Field::Text
34
- expect(@fields.fetch(:ball)).to be_instance_of Field::Null
35
- expect(@fields.fetch(:car)).to be_instance_of Field::Text
36
- end
55
+ expect(form).to receive(:name)
37
56
 
38
- it 'assigns the fieldset to the field' do
39
- expect(@fields.fetch(:name).fieldset).to eq :general
40
- expect(@fields.fetch(:ball).fieldset).to eq :toys
41
- expect(@fields.fetch(:car).fieldset).to eq :toys
57
+ form.model_name
58
+ end
59
+
60
+ it 'returns the correct model name' do
61
+ form = Class.new { include SuperForm }
62
+ form.stub(:name).and_return('InexistentForm')
63
+
64
+ expect(form.model_name.name).to eq 'Inexistent'
65
+ end
42
66
  end
43
67
  end
44
68
 
45
- context 'when a form object is created' do
46
- before(:each) { @dummy_form = DummyForm.new }
69
+ context 'when fields and fieldsets are defined' do
70
+ let!(:dummy) { form_with_fields }
47
71
 
48
- # this can get better, but for now it's ok
49
72
  it 'creates virtus attributes' do
50
- expect(@dummy_form).to respond_to(:name)
51
- expect(@dummy_form).to respond_to(:name=)
52
- expect(@dummy_form).to respond_to(:ball)
53
- expect(@dummy_form).to respond_to(:ball=)
54
- expect(@dummy_form).to respond_to(:car)
55
- expect(@dummy_form).to respond_to(:car=)
73
+ expect(dummy).to respond_to(:name)
74
+ expect(dummy).to respond_to(:name=)
75
+ expect(dummy).to respond_to(:ball)
76
+ expect(dummy).to respond_to(:ball=)
77
+ expect(dummy).to respond_to(:car)
78
+ expect(dummy).to respond_to(:car=)
79
+ expect(dummy).to respond_to(:email)
80
+ expect(dummy).to respond_to(:email=)
81
+ expect(dummy).to respond_to(:phone)
82
+ expect(dummy).to respond_to(:phone=)
56
83
  end
57
84
 
58
- it 'assigns the fieldset id to the field' do
59
- expect(@dummy_form.field(:name).fieldset).to eq :general
60
- expect(@dummy_form.field(:ball).fieldset).to eq :toys
61
- expect(@dummy_form.field(:car).fieldset).to eq :toys
85
+ it 'is ready for validation' do
86
+ expect(dummy).to_not be_valid
87
+ expect(dummy.errors[:name]).to eq ["can't be blank"]
88
+ expect(dummy.errors[:phone]).to eq ["is too short (minimum is 4 characters)"]
62
89
  end
63
90
 
64
- it 'defines fieldsets containing the bundled fields' do
65
- general = @dummy_form.fieldsets.fetch(:general)
91
+ it 'calls persisted! when a valid form is saved' do
92
+ dummy.name = 'Dummy'
93
+ dummy.phone = '12345'
66
94
 
67
- expect(general.first).to be_instance_of Field::Text
68
- expect(general.first.name).to eq :name
95
+ expect(dummy).to receive(:persist!).once
96
+ dummy.save
97
+ end
98
+ end
69
99
 
70
- toys = @dummy_form.fieldsets.fetch(:toys)
100
+ context 'when callbacks are defined' do
101
+ it 'triggers callbacks for save' do
102
+ dummy = form_with_fields do
103
+ before_save :callback_call
104
+ after_save :callback_call
105
+ end
71
106
 
72
- expect(toys.first).to be_instance_of Field::Null
73
- expect(toys.first.name).to eq :ball
107
+ dummy.name = 'Dummy'
108
+ dummy.phone = '12345'
74
109
 
75
- expect(toys.last).to be_instance_of Field::Text
76
- expect(toys.last.name).to eq :car
110
+ expect(dummy).to receive(:callback_call).twice
111
+ dummy.save
77
112
  end
78
113
 
79
- it 'fieldsets fields are the same instance of fields' do
80
- @dummy_form.fieldsets.each do |id, fields|
81
- fields.each do |field|
82
- expect(field).to eq @dummy_form.field(field.name)
83
- end
114
+ it 'triggers callbacks for validation' do
115
+ dummy = form_with_fields do
116
+ before_validation :callback_call
117
+ after_validation :callback_call
84
118
  end
119
+
120
+ dummy.name = 'Dummy'
121
+ dummy.phone = '12345'
122
+
123
+ expect(dummy).to receive(:callback_call).twice
124
+ dummy.valid?
85
125
  end
126
+ end
127
+ end
86
128
 
87
- it 'is ready for validation' do
88
- expect(@dummy_form).to_not be_valid
89
- expect(@dummy_form.errors[:name]).to eq ["can't be blank"]
129
+ def form_with_fields(&block)
130
+ Class.new do
131
+ include SuperForm
132
+
133
+ fieldset :general do
134
+ field :name, Field::Text, presence: true
90
135
  end
91
136
 
92
- context 'when assigning a new attribute' do
93
- it 'assigns the value of the attribute to the field' do
94
- pending
95
- end
137
+ fieldset :toys do
138
+ field :ball, Field::Bare
139
+ field :car, Field::Text
96
140
  end
97
- end
141
+
142
+ field :email, Field::Text
143
+ field :phone, Field::Text, length: { minimum: 4 }
144
+
145
+ instance_eval(&block) if block_given?
146
+ end.new
147
+ end
148
+
149
+ def form_with_no_fields
150
+ Class.new do
151
+ include SuperForm
152
+ end.new
98
153
  end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'telephone'
3
+
4
+ describe Telephone do
5
+ it "returns true if valid" do
6
+ valid_numbers = ['8436429999', '(84) 1111-1111', '8498741122']
7
+
8
+ valid_numbers.each do |number|
9
+ telephone = Telephone.new(number)
10
+ expect(telephone).to be_valid
11
+ end
12
+ end
13
+
14
+ it "returns false if not valid" do
15
+ invalid_numbers = ['843649999', '843 aaa', 'aaa 1234 $%']
16
+
17
+ invalid_numbers.each do |number|
18
+ telephone = Telephone.new(number)
19
+ expect(telephone).to_not be_valid
20
+ end
21
+ end
22
+
23
+ it "returns a stripped number" do
24
+ telephone = Telephone.new('(84) 3642-9999')
25
+ expect(telephone.stripped).to eq('8436429999')
26
+ end
27
+
28
+ it "returns a formatted number" do
29
+ telephone = Telephone.new('84 3642-9999')
30
+ expect(telephone.formatted).to eq('(84) 3642-9999')
31
+ end
32
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,17 +1,10 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
1
+ require 'pry'
2
+
7
3
  RSpec.configure do |config|
8
4
  config.treat_symbols_as_metadata_keys_with_true_values = true
9
5
  config.run_all_when_everything_filtered = true
10
6
  config.filter_run :focus
11
-
12
- # Run specs in random order to surface order dependencies. If you find an
13
- # order dependency and want to debug it, you can fix the order by providing
14
- # the seed, which is printed after each run.
15
- # --seed 1234
16
7
  config.order = 'random'
17
8
  end
9
+
10
+ I18n.enforce_available_locales = false if defined?(I18n)
@@ -0,0 +1,28 @@
1
+ shared_examples "presenter nameable" do
2
+ describe '.name' do
3
+ it 'returns a demodulized and underscored presenter name' do
4
+ presenter = Class.new(described_class) do
5
+ def self.name
6
+ 'Presenter::Deep::IDontExist'
7
+ end
8
+ end
9
+
10
+ expect(presenter.__name__).to eq 'i_dont_exist'
11
+ end
12
+
13
+ it 'trims _presenter suffix' do
14
+ presenter = Class.new(described_class) do
15
+ def self.name
16
+ 'Presenter::Deep::IDontExistPresenter'
17
+ end
18
+ end
19
+
20
+ expect(presenter.__name__).to eq 'i_dont_exist'
21
+ end
22
+
23
+ it 'raises an exception if presenter is anonymous' do
24
+ presenter = Class.new(described_class)
25
+ expect { presenter.__name__ }.to raise_error RuntimeError
26
+ end
27
+ end
28
+ end
data/super_form.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'rspec', '2.14.1'
25
25
  spec.add_development_dependency 'simplecov', '0.8.2'
26
26
  spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'pry', '0.9.12.6'
27
28
  spec.add_development_dependency 'capybara', '2.2.0'
28
29
  spec.add_development_dependency 'activemodel', '4.0.2'
29
30
  spec.add_development_dependency 'activesupport', '4.0.2'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: super_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thiago A. Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-09 00:00:00.000000000 Z
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.9.12.6
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 0.9.12.6
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: capybara
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -191,24 +205,53 @@ files:
191
205
  - lib/cnpj_validator.rb
192
206
  - lib/cpf_validator.rb
193
207
  - lib/field.rb
208
+ - lib/field/bare.rb
194
209
  - lib/field/base.rb
195
210
  - lib/field/cnpj.rb
196
211
  - lib/field/cpf.rb
197
212
  - lib/field/email.rb
198
213
  - lib/field/error.rb
214
+ - lib/field/float.rb
215
+ - lib/field/form.rb
216
+ - lib/field/form_array.rb
217
+ - lib/field/form_proxy.rb
199
218
  - lib/field/password.rb
200
219
  - lib/field/person_type.rb
201
220
  - lib/field/telephone.rb
202
221
  - lib/field/text.rb
203
222
  - lib/person_type.rb
223
+ - lib/presenter.rb
224
+ - lib/presenter/cnpj.rb
225
+ - lib/presenter/cpf.rb
226
+ - lib/presenter/form.rb
227
+ - lib/presenter/password.rb
228
+ - lib/presenter/person_type.rb
229
+ - lib/presenter/telephone.rb
204
230
  - lib/super_form.rb
231
+ - lib/super_form/fieldable.rb
232
+ - lib/super_form/fieldset.rb
205
233
  - lib/super_form/version.rb
206
234
  - lib/tasks/.keep
207
235
  - lib/telephone.rb
208
236
  - lib/telephone_validator.rb
209
237
  - lib/uniqueness_validator.rb
238
+ - spec/lib/field/form_array.rb
239
+ - spec/lib/field/form_array_spec.rb
240
+ - spec/lib/field/form_spec.rb
241
+ - spec/lib/field_spec.rb
242
+ - spec/lib/person_type_spec.rb
243
+ - spec/lib/presenter/cnpj_spec.rb
244
+ - spec/lib/presenter/cpf_spec.rb
245
+ - spec/lib/presenter/form_spec.rb
246
+ - spec/lib/presenter/password_spec.rb
247
+ - spec/lib/presenter/person_type_spec.rb
248
+ - spec/lib/presenter/telephone_spec.rb
249
+ - spec/lib/presenter_spec.rb
250
+ - spec/lib/super_form/fieldable_spec.rb
210
251
  - spec/lib/super_form_spec.rb
252
+ - spec/lib/telephone_spec.rb
211
253
  - spec/spec_helper.rb
254
+ - spec/support/presenter_name.rb
212
255
  - super_form.gemspec
213
256
  homepage: http://github.com/thiagoa/super_form
214
257
  licenses:
@@ -235,6 +278,21 @@ signing_key:
235
278
  specification_version: 4
236
279
  summary: Object oriented forms for Rails
237
280
  test_files:
281
+ - spec/lib/field/form_array.rb
282
+ - spec/lib/field/form_array_spec.rb
283
+ - spec/lib/field/form_spec.rb
284
+ - spec/lib/field_spec.rb
285
+ - spec/lib/person_type_spec.rb
286
+ - spec/lib/presenter/cnpj_spec.rb
287
+ - spec/lib/presenter/cpf_spec.rb
288
+ - spec/lib/presenter/form_spec.rb
289
+ - spec/lib/presenter/password_spec.rb
290
+ - spec/lib/presenter/person_type_spec.rb
291
+ - spec/lib/presenter/telephone_spec.rb
292
+ - spec/lib/presenter_spec.rb
293
+ - spec/lib/super_form/fieldable_spec.rb
238
294
  - spec/lib/super_form_spec.rb
295
+ - spec/lib/telephone_spec.rb
239
296
  - spec/spec_helper.rb
297
+ - spec/support/presenter_name.rb
240
298
  has_rdoc: