granite-form 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +13 -0
  3. data/.github/workflows/ci.yml +35 -0
  4. data/.github/workflows/main.yml +29 -0
  5. data/.gitignore +21 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +64 -0
  8. data/.rubocop_todo.yml +48 -0
  9. data/Appraisals +8 -0
  10. data/CHANGELOG.md +73 -0
  11. data/Gemfile +8 -0
  12. data/Guardfile +77 -0
  13. data/LICENSE +22 -0
  14. data/README.md +429 -0
  15. data/Rakefile +6 -0
  16. data/gemfiles/rails.4.2.gemfile +15 -0
  17. data/gemfiles/rails.5.0.gemfile +15 -0
  18. data/gemfiles/rails.5.1.gemfile +15 -0
  19. data/gemfiles/rails.5.2.gemfile +15 -0
  20. data/gemfiles/rails.6.0.gemfile +14 -0
  21. data/gemfiles/rails.6.1.gemfile +14 -0
  22. data/gemfiles/rails.7.0.gemfile +14 -0
  23. data/granite-form.gemspec +31 -0
  24. data/lib/granite/form/active_record/associations.rb +57 -0
  25. data/lib/granite/form/active_record/nested_attributes.rb +20 -0
  26. data/lib/granite/form/base.rb +15 -0
  27. data/lib/granite/form/config.rb +42 -0
  28. data/lib/granite/form/errors.rb +111 -0
  29. data/lib/granite/form/extensions.rb +36 -0
  30. data/lib/granite/form/model/associations/base.rb +97 -0
  31. data/lib/granite/form/model/associations/collection/embedded.rb +14 -0
  32. data/lib/granite/form/model/associations/collection/proxy.rb +35 -0
  33. data/lib/granite/form/model/associations/embeds_any.rb +19 -0
  34. data/lib/granite/form/model/associations/embeds_many.rb +152 -0
  35. data/lib/granite/form/model/associations/embeds_one.rb +112 -0
  36. data/lib/granite/form/model/associations/nested_attributes.rb +215 -0
  37. data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb +33 -0
  38. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +68 -0
  39. data/lib/granite/form/model/associations/persistence_adapters/base.rb +55 -0
  40. data/lib/granite/form/model/associations/references_any.rb +43 -0
  41. data/lib/granite/form/model/associations/references_many.rb +113 -0
  42. data/lib/granite/form/model/associations/references_one.rb +88 -0
  43. data/lib/granite/form/model/associations/reflections/base.rb +92 -0
  44. data/lib/granite/form/model/associations/reflections/embeds_any.rb +52 -0
  45. data/lib/granite/form/model/associations/reflections/embeds_many.rb +17 -0
  46. data/lib/granite/form/model/associations/reflections/embeds_one.rb +19 -0
  47. data/lib/granite/form/model/associations/reflections/references_any.rb +65 -0
  48. data/lib/granite/form/model/associations/reflections/references_many.rb +30 -0
  49. data/lib/granite/form/model/associations/reflections/references_one.rb +32 -0
  50. data/lib/granite/form/model/associations/reflections/singular.rb +37 -0
  51. data/lib/granite/form/model/associations/validations.rb +41 -0
  52. data/lib/granite/form/model/associations.rb +120 -0
  53. data/lib/granite/form/model/attributes/attribute.rb +75 -0
  54. data/lib/granite/form/model/attributes/base.rb +134 -0
  55. data/lib/granite/form/model/attributes/collection.rb +19 -0
  56. data/lib/granite/form/model/attributes/dictionary.rb +28 -0
  57. data/lib/granite/form/model/attributes/localized.rb +44 -0
  58. data/lib/granite/form/model/attributes/reference_many.rb +21 -0
  59. data/lib/granite/form/model/attributes/reference_one.rb +52 -0
  60. data/lib/granite/form/model/attributes/reflections/attribute.rb +61 -0
  61. data/lib/granite/form/model/attributes/reflections/base.rb +62 -0
  62. data/lib/granite/form/model/attributes/reflections/collection.rb +12 -0
  63. data/lib/granite/form/model/attributes/reflections/dictionary.rb +15 -0
  64. data/lib/granite/form/model/attributes/reflections/localized.rb +45 -0
  65. data/lib/granite/form/model/attributes/reflections/reference_many.rb +12 -0
  66. data/lib/granite/form/model/attributes/reflections/reference_one.rb +49 -0
  67. data/lib/granite/form/model/attributes/reflections/represents.rb +56 -0
  68. data/lib/granite/form/model/attributes/represents.rb +67 -0
  69. data/lib/granite/form/model/attributes.rb +204 -0
  70. data/lib/granite/form/model/callbacks.rb +72 -0
  71. data/lib/granite/form/model/conventions.rb +40 -0
  72. data/lib/granite/form/model/dirty.rb +84 -0
  73. data/lib/granite/form/model/lifecycle.rb +309 -0
  74. data/lib/granite/form/model/localization.rb +26 -0
  75. data/lib/granite/form/model/persistence.rb +59 -0
  76. data/lib/granite/form/model/primary.rb +59 -0
  77. data/lib/granite/form/model/representation.rb +101 -0
  78. data/lib/granite/form/model/scopes.rb +118 -0
  79. data/lib/granite/form/model/validations/associated.rb +22 -0
  80. data/lib/granite/form/model/validations/nested.rb +56 -0
  81. data/lib/granite/form/model/validations.rb +29 -0
  82. data/lib/granite/form/model.rb +33 -0
  83. data/lib/granite/form/railtie.rb +9 -0
  84. data/lib/granite/form/undefined_class.rb +11 -0
  85. data/lib/granite/form/version.rb +5 -0
  86. data/lib/granite/form.rb +163 -0
  87. data/spec/lib/granite/form/active_record/associations_spec.rb +211 -0
  88. data/spec/lib/granite/form/active_record/nested_attributes_spec.rb +15 -0
  89. data/spec/lib/granite/form/config_spec.rb +66 -0
  90. data/spec/lib/granite/form/model/associations/embeds_many_spec.rb +706 -0
  91. data/spec/lib/granite/form/model/associations/embeds_one_spec.rb +533 -0
  92. data/spec/lib/granite/form/model/associations/nested_attributes_spec.rb +119 -0
  93. data/spec/lib/granite/form/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  94. data/spec/lib/granite/form/model/associations/references_many_spec.rb +572 -0
  95. data/spec/lib/granite/form/model/associations/references_one_spec.rb +445 -0
  96. data/spec/lib/granite/form/model/associations/reflections/embeds_any_spec.rb +42 -0
  97. data/spec/lib/granite/form/model/associations/reflections/embeds_many_spec.rb +145 -0
  98. data/spec/lib/granite/form/model/associations/reflections/embeds_one_spec.rb +117 -0
  99. data/spec/lib/granite/form/model/associations/reflections/references_many_spec.rb +303 -0
  100. data/spec/lib/granite/form/model/associations/reflections/references_one_spec.rb +287 -0
  101. data/spec/lib/granite/form/model/associations/validations_spec.rb +137 -0
  102. data/spec/lib/granite/form/model/associations_spec.rb +198 -0
  103. data/spec/lib/granite/form/model/attributes/attribute_spec.rb +186 -0
  104. data/spec/lib/granite/form/model/attributes/base_spec.rb +97 -0
  105. data/spec/lib/granite/form/model/attributes/collection_spec.rb +72 -0
  106. data/spec/lib/granite/form/model/attributes/dictionary_spec.rb +100 -0
  107. data/spec/lib/granite/form/model/attributes/localized_spec.rb +103 -0
  108. data/spec/lib/granite/form/model/attributes/reflections/attribute_spec.rb +72 -0
  109. data/spec/lib/granite/form/model/attributes/reflections/base_spec.rb +56 -0
  110. data/spec/lib/granite/form/model/attributes/reflections/collection_spec.rb +37 -0
  111. data/spec/lib/granite/form/model/attributes/reflections/dictionary_spec.rb +43 -0
  112. data/spec/lib/granite/form/model/attributes/reflections/localized_spec.rb +37 -0
  113. data/spec/lib/granite/form/model/attributes/reflections/represents_spec.rb +70 -0
  114. data/spec/lib/granite/form/model/attributes/represents_spec.rb +85 -0
  115. data/spec/lib/granite/form/model/attributes_spec.rb +350 -0
  116. data/spec/lib/granite/form/model/callbacks_spec.rb +337 -0
  117. data/spec/lib/granite/form/model/conventions_spec.rb +11 -0
  118. data/spec/lib/granite/form/model/dirty_spec.rb +84 -0
  119. data/spec/lib/granite/form/model/lifecycle_spec.rb +356 -0
  120. data/spec/lib/granite/form/model/persistence_spec.rb +46 -0
  121. data/spec/lib/granite/form/model/primary_spec.rb +84 -0
  122. data/spec/lib/granite/form/model/representation_spec.rb +139 -0
  123. data/spec/lib/granite/form/model/scopes_spec.rb +86 -0
  124. data/spec/lib/granite/form/model/typecasting_spec.rb +193 -0
  125. data/spec/lib/granite/form/model/validations/associated_spec.rb +102 -0
  126. data/spec/lib/granite/form/model/validations/nested_spec.rb +164 -0
  127. data/spec/lib/granite/form/model/validations_spec.rb +31 -0
  128. data/spec/lib/granite/form/model_spec.rb +10 -0
  129. data/spec/lib/granite/form_spec.rb +11 -0
  130. data/spec/shared/nested_attribute_examples.rb +332 -0
  131. data/spec/spec_helper.rb +50 -0
  132. data/spec/support/model_helpers.rb +10 -0
  133. data/spec/support/muffle_helper.rb +7 -0
  134. metadata +403 -0
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Scopes do
4
+ let(:model) do
5
+ stub_model do
6
+ include Granite::Form::Model::Scopes
7
+
8
+ attribute :name, String
9
+
10
+ class << self
11
+ def except_first
12
+ scope[1..-1]
13
+ end
14
+
15
+ def no_mars
16
+ scope.delete_if { |i| i.name == 'Mars' }
17
+ end
18
+
19
+ private
20
+
21
+ def hidden_method() end
22
+ end
23
+ end
24
+ end
25
+
26
+ let(:hash_scoped_model) do
27
+ stub_model do
28
+ include Granite::Form::Model::Scopes
29
+ scopify Hash
30
+ end
31
+ end
32
+
33
+ describe '.scope_class' do
34
+ specify { expect(model.scope_class).to be < Array }
35
+ specify { expect(model.scope_class).to eq(model::ScopeProxy) }
36
+ specify { expect(model.scope_class._scope_model).to eq(model) }
37
+ specify { expect(model.scope_class.new).to be_empty }
38
+
39
+ specify { expect(hash_scoped_model.scope_class).to be < Hash }
40
+ specify { expect(hash_scoped_model.scope_class).to eq(hash_scoped_model::ScopeProxy) }
41
+ specify { expect(hash_scoped_model.scope_class._scope_model).to eq(hash_scoped_model) }
42
+ specify { expect(hash_scoped_model.scope_class.new).to be_empty }
43
+
44
+ context do
45
+ let(:scope) { model.scope([model.new(name: 'Hello'), model.new(name: 'World'), model.new(name: 'Mars')]) }
46
+
47
+ specify { expect(scope).to be_instance_of model.scope_class }
48
+ specify { expect { model.scope([model.new(name: 'Hello'), {}]) }.to raise_error Granite::Form::AssociationTypeMismatch }
49
+
50
+ context 'scopes' do
51
+ specify { expect(scope.except_first).to be_instance_of model.scope_class }
52
+ specify { expect(scope.no_mars).to be_instance_of model.scope_class }
53
+ specify { expect(scope.except_first).to eq(model.scope([model.new(name: 'World'), model.new(name: 'Mars')])) }
54
+ specify { expect(scope.no_mars).to eq(model.scope([model.new(name: 'Hello'), model.new(name: 'World')])) }
55
+ specify { expect(scope.except_first.no_mars).to eq(model.scope([model.new(name: 'World')])) }
56
+ specify { expect(scope.no_mars.except_first).to eq(model.scope([model.new(name: 'World')])) }
57
+ specify { expect { scope.hidden_method }.to raise_error NoMethodError }
58
+
59
+ specify { expect(scope).to respond_to(:no_mars) }
60
+ specify { expect(scope).to respond_to(:except_first) }
61
+ specify { expect(scope).not_to respond_to(:hidden_method) }
62
+ end
63
+ end
64
+ end
65
+
66
+ context do
67
+ let!(:ancestor) do
68
+ stub_model do
69
+ include Granite::Form::Model::Scopes
70
+ end
71
+ end
72
+
73
+ let!(:descendant1) do
74
+ Class.new ancestor
75
+ end
76
+
77
+ let!(:descendant2) do
78
+ Class.new ancestor
79
+ end
80
+
81
+ specify { expect(descendant1.scope_class).to be < Array }
82
+ specify { expect(descendant2.scope_class).to be < Array }
83
+ specify { expect(ancestor.scope_class).not_to eq(descendant1.scope_class) }
84
+ specify { expect(descendant1.scope_class).not_to eq(descendant2.scope_class) }
85
+ end
86
+ end
@@ -0,0 +1,193 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Attributes do
4
+ subject { model.new }
5
+
6
+ context 'object' do
7
+ before { stub_class(:descendant) }
8
+ let(:model) { stub_model { attribute :column, Object } }
9
+
10
+ specify { expect(model.new(column: 'hello').column).to eq('hello') }
11
+ specify { expect(model.new(column: []).column).to eq([]) }
12
+ specify { expect(model.new(column: Descendant.new).column).to be_a(Descendant) }
13
+ specify { expect(model.new(column: Object.new).column).to be_a(Object) }
14
+ specify { expect(model.new(column: nil).column).to be_nil }
15
+
16
+ context do
17
+ before { stub_class(:descendant2, Descendant) }
18
+ let(:model) { stub_model { attribute :column, Descendant } }
19
+
20
+ specify { expect(model.new(column: 'hello').column).to be_nil }
21
+ specify { expect(model.new(column: []).column).to be_nil }
22
+ specify { expect(model.new(column: Descendant.new).column).to be_a(Descendant) }
23
+ specify { expect(model.new(column: Descendant2.new).column).to be_a(Descendant2) }
24
+ specify { expect(model.new(column: Object.new).column).to be_nil }
25
+ specify { expect(model.new(column: nil).column).to be_nil }
26
+ end
27
+ end
28
+
29
+ context 'string' do
30
+ let(:model) { stub_model { attribute :column, String } }
31
+
32
+ specify { expect(model.new(column: 'hello').column).to eq('hello') }
33
+ specify { expect(model.new(column: 123).column).to eq('123') }
34
+ specify { expect(model.new(column: nil).column).to be_nil }
35
+ end
36
+
37
+ context 'integer' do
38
+ let(:model) { stub_model { attribute :column, Integer } }
39
+
40
+ specify { expect(model.new(column: 'hello').column).to be_nil }
41
+ specify { expect(model.new(column: '123hello').column).to be_nil }
42
+ specify { expect(model.new(column: '123').column).to eq(123) }
43
+ specify { expect(model.new(column: '123.5').column).to eq(123) }
44
+ specify { expect(model.new(column: 123).column).to eq(123) }
45
+ specify { expect(model.new(column: 123.5).column).to eq(123) }
46
+ specify { expect(model.new(column: nil).column).to be_nil }
47
+ specify { expect(model.new(column: [123]).column).to be_nil }
48
+ end
49
+
50
+ context 'float' do
51
+ let(:model) { stub_model { attribute :column, Float } }
52
+
53
+ specify { expect(model.new(column: 'hello').column).to be_nil }
54
+ specify { expect(model.new(column: '123hello').column).to be_nil }
55
+ specify { expect(model.new(column: '123').column).to eq(123.0) }
56
+ specify { expect(model.new(column: '123.').column).to be_nil }
57
+ specify { expect(model.new(column: '123.5').column).to eq(123.5) }
58
+ specify { expect(model.new(column: 123).column).to eq(123.0) }
59
+ specify { expect(model.new(column: 123.5).column).to eq(123.5) }
60
+ specify { expect(model.new(column: nil).column).to be_nil }
61
+ specify { expect(model.new(column: [123.5]).column).to be_nil }
62
+ end
63
+
64
+ context 'big_decimal' do
65
+ let(:model) { stub_model { attribute :column, BigDecimal } }
66
+
67
+ specify { expect(model.new(column: 'hello').column).to be_nil }
68
+ specify { expect(model.new(column: '123hello').column).to be_nil }
69
+ specify { expect(model.new(column: '123').column).to eq(BigDecimal('123.0')) }
70
+ specify { expect(model.new(column: '123.').column).to be_nil }
71
+ specify { expect(model.new(column: '123.5').column).to eq(BigDecimal('123.5')) }
72
+ specify { expect(model.new(column: 123).column).to eq(BigDecimal('123.0')) }
73
+ specify { expect(model.new(column: 123.5).column).to eq(BigDecimal('123.5')) }
74
+ specify { expect(model.new(column: nil).column).to be_nil }
75
+ specify { expect(model.new(column: [123.5]).column).to be_nil }
76
+ end
77
+
78
+ context 'boolean' do
79
+ let(:model) { stub_model { attribute :column, Boolean } }
80
+
81
+ specify { expect(model.new(column: 'hello').column).to be_nil }
82
+ specify { expect(model.new(column: 'true').column).to eq(true) }
83
+ specify { expect(model.new(column: 'false').column).to eq(false) }
84
+ specify { expect(model.new(column: '1').column).to eq(true) }
85
+ specify { expect(model.new(column: '0').column).to eq(false) }
86
+ specify { expect(model.new(column: true).column).to eq(true) }
87
+ specify { expect(model.new(column: false).column).to eq(false) }
88
+ specify { expect(model.new(column: 1).column).to eq(true) }
89
+ specify { expect(model.new(column: 0).column).to eq(false) }
90
+ specify { expect(model.new(column: nil).column).to be_nil }
91
+ specify { expect(model.new(column: [123]).column).to be_nil }
92
+ end
93
+
94
+ context 'array' do
95
+ let(:model) { stub_model { attribute :column, Array } }
96
+
97
+ specify { expect(model.new(column: [1, 2, 3]).column).to eq([1, 2, 3]) }
98
+ specify { expect(model.new(column: 'hello, world').column).to eq(%w[hello world]) }
99
+ specify { expect(model.new(column: 10).column).to be_nil }
100
+ end
101
+
102
+ # rubocop:disable Style/DateTime
103
+ context 'date' do
104
+ let(:model) { stub_model { attribute :column, Date } }
105
+ let(:date) { Date.new(2013, 6, 13) }
106
+
107
+ specify { expect(model.new(column: nil).column).to be_nil }
108
+ specify { expect(model.new(column: '2013-06-13').column).to eq(date) }
109
+ specify { expect(model.new(column: '2013-55-55').column).to be_nil }
110
+ specify { expect(model.new(column: 'blablabla').column).to be_nil }
111
+ specify { expect(model.new(column: DateTime.new(2013, 6, 13, 23, 13)).column).to eq(date) }
112
+ specify { expect(model.new(column: Time.new(2013, 6, 13, 23, 13)).column).to eq(date) }
113
+ specify { expect(model.new(column: Date.new(2013, 6, 13)).column).to eq(date) }
114
+ end
115
+
116
+ context 'datetime' do
117
+ let(:model) { stub_model { attribute :column, DateTime } }
118
+ let(:datetime) { DateTime.new(2013, 6, 13, 23, 13) }
119
+
120
+ specify { expect(model.new(column: nil).column).to be_nil }
121
+ specify { expect(model.new(column: '2013-06-13 23:13').column).to eq(datetime) }
122
+ specify { expect(model.new(column: '2013-55-55 55:55').column).to be_nil }
123
+ specify { expect(model.new(column: 'blablabla').column).to be_nil }
124
+ specify { expect(model.new(column: Date.new(2013, 6, 13)).column).to eq(DateTime.new(2013, 6, 13, 0, 0)) }
125
+ specify { expect(model.new(column: Time.utc(2013, 6, 13, 23, 13).utc).column).to eq(DateTime.new(2013, 6, 13, 23, 13)) }
126
+ specify { expect(model.new(column: DateTime.new(2013, 6, 13, 23, 13)).column).to eq(DateTime.new(2013, 6, 13, 23, 13)) }
127
+ end
128
+
129
+ context 'time' do
130
+ let(:model) { stub_model { attribute :column, Time } }
131
+
132
+ specify { expect(model.new(column: nil).column).to be_nil }
133
+ specify { expect(model.new(column: '2013-06-13 23:13').column).to eq('2013-06-13 23:13'.to_time) }
134
+ specify { expect(model.new(column: '2013-55-55 55:55').column).to be_nil }
135
+ specify { expect(model.new(column: 'blablabla').column).to be_nil }
136
+ specify { expect(model.new(column: Date.new(2013, 6, 13)).column).to eq(Time.new(2013, 6, 13, 0, 0)) }
137
+ specify { expect(model.new(column: DateTime.new(2013, 6, 13, 19, 13)).column).to eq(DateTime.new(2013, 6, 13, 19, 13).to_time) }
138
+ specify { expect(model.new(column: Time.new(2013, 6, 13, 23, 13)).column).to eq(Time.new(2013, 6, 13, 23, 13)) }
139
+
140
+ context 'Time.zone set' do
141
+ around { |example| Time.use_zone('Bangkok', &example) }
142
+
143
+ specify { expect(model.new(column: nil).column).to be_nil }
144
+ specify { expect(model.new(column: '2013-06-13 23:13').column).to eq(Time.zone.parse('2013-06-13 23:13')) }
145
+ specify { expect(model.new(column: '2013-55-55 55:55').column).to be_nil }
146
+ specify { expect(model.new(column: 'blablabla').column).to be_nil }
147
+ specify { expect(model.new(column: Date.new(2013, 6, 13)).column).to eq(Time.new(2013, 6, 13, 0, 0)) }
148
+ specify { expect(model.new(column: DateTime.new(2013, 6, 13, 19, 13)).column).to eq(DateTime.new(2013, 6, 13, 19, 13).to_time) }
149
+ specify { expect(model.new(column: Time.new(2013, 6, 13, 23, 13)).column).to eq(Time.new(2013, 6, 13, 23, 13)) }
150
+ end
151
+ end
152
+ # rubocop:enable Style/DateTime
153
+
154
+ context 'time_zone' do
155
+ let(:model) { stub_model { attribute :column, ActiveSupport::TimeZone } }
156
+
157
+ specify { expect(model.new(column: nil).column).to be_nil }
158
+ specify { expect(model.new(column: Object.new).column).to be_nil }
159
+ specify { expect(model.new(column: Time.now).column).to be_nil }
160
+ specify { expect(model.new(column: 'blablabla').column).to be_nil }
161
+ specify { expect(model.new(column: TZInfo::Timezone.all.first).column).to be_a ActiveSupport::TimeZone }
162
+ specify { expect(model.new(column: 'Moscow').column).to be_a ActiveSupport::TimeZone }
163
+ specify { expect(model.new(column: '+4').column).to be_a ActiveSupport::TimeZone }
164
+ specify { expect(model.new(column: '-3').column).to be_a ActiveSupport::TimeZone }
165
+ specify { expect(model.new(column: '3600').column).to be_a ActiveSupport::TimeZone }
166
+ specify { expect(model.new(column: '-7200').column).to be_a ActiveSupport::TimeZone }
167
+ specify { expect(model.new(column: 4).column).to be_a ActiveSupport::TimeZone }
168
+ specify { expect(model.new(column: -3).column).to be_a ActiveSupport::TimeZone }
169
+ specify { expect(model.new(column: 3600).column).to be_a ActiveSupport::TimeZone }
170
+ specify { expect(model.new(column: -7200).column).to be_a ActiveSupport::TimeZone }
171
+ end
172
+
173
+ context 'uuid' do
174
+ let(:model) { stub_model { attribute :column, Granite::Form::UUID } }
175
+ let(:uuid) { Granite::Form::UUID.random_create }
176
+ let(:uuid_tools) { UUIDTools::UUID.random_create }
177
+
178
+ specify { expect(uuid.as_json).to eq(uuid.to_s) }
179
+ specify { expect(uuid.to_json).to eq("\"#{uuid}\"") }
180
+ specify { expect(uuid.to_param).to eq(uuid.to_s) }
181
+ specify { expect(uuid.to_query(:key)).to eq("key=#{uuid}") }
182
+
183
+ specify { expect(model.new(column: nil).column).to be_nil }
184
+ specify { expect(model.new(column: Object.new).column).to be_nil }
185
+ specify { expect(model.new(column: uuid_tools).column).to be_a Granite::Form::UUID }
186
+ specify { expect(model.new(column: uuid_tools).column).to eq(uuid_tools) }
187
+ specify { expect(model.new(column: uuid).column).to eq(uuid) }
188
+ specify { expect(model.new(column: uuid.to_s).column).to eq(uuid) }
189
+ specify { expect(model.new(column: uuid.to_i).column).to eq(uuid) }
190
+ specify { expect(model.new(column: uuid.hexdigest).column).to eq(uuid) }
191
+ specify { expect(model.new(column: uuid.raw).column).to eq(uuid) }
192
+ end
193
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Validations::AssociatedValidator do
4
+ before do
5
+ stub_model(:validated_assoc) do
6
+ include Granite::Form::Model
7
+ include Granite::Form::Model::Lifecycle
8
+
9
+ attribute :name, String
10
+
11
+ validates_presence_of :name
12
+ end
13
+
14
+ stub_model(:unvalidated_assoc) do
15
+ include Granite::Form::Model
16
+ include Granite::Form::Model::Lifecycle
17
+
18
+ attribute :name, String
19
+ end
20
+
21
+ stub_model(:main) do
22
+ include Granite::Form::Model::Persistence
23
+ include Granite::Form::Model::Associations
24
+
25
+ attribute :name, String
26
+
27
+ validates_presence_of :name
28
+ validates_associated :validated_one, :unvalidated_one, :validated_many, :unvalidated_many
29
+
30
+ embeds_one :validated_one, validate: false, class_name: 'ValidatedAssoc'
31
+ embeds_one :unvalidated_one, class_name: 'UnvalidatedAssoc'
32
+ embeds_many :validated_many, class_name: 'ValidatedAssoc'
33
+ embeds_many :unvalidated_many, class_name: 'UnvalidatedAssoc'
34
+ end
35
+ end
36
+
37
+ context do
38
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {name: 'name'} }
39
+ it { is_expected.to be_valid }
40
+ end
41
+
42
+ context do
43
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {} }
44
+ it { is_expected.not_to be_valid }
45
+ specify do
46
+ expect { instance.validate }.to change { instance.errors.messages }
47
+ .to(validated_one: ['is invalid'])
48
+ end
49
+ end
50
+
51
+ context do
52
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {name: 'name'} }
53
+ it { is_expected.to be_valid }
54
+ end
55
+
56
+ context do
57
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {} }
58
+ it { is_expected.to be_valid }
59
+ end
60
+
61
+ context do
62
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}] }
63
+ it { is_expected.to be_valid }
64
+ end
65
+
66
+ context do
67
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}] }
68
+ it { is_expected.not_to be_valid }
69
+ specify do
70
+ expect { instance.validate }.to change { instance.errors.messages }
71
+ .to('validated_many.0.name': ["can't be blank"], validated_many: ['is invalid'])
72
+ end
73
+ end
74
+
75
+ context do
76
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{name: 'name'}] }
77
+ it { is_expected.to be_valid }
78
+ end
79
+
80
+ context do
81
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{}] }
82
+ it { is_expected.to be_valid }
83
+ end
84
+
85
+ context do
86
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}], validated_one: {} }
87
+ it { is_expected.not_to be_valid }
88
+ specify do
89
+ expect { instance.validate }.to change { instance.errors.messages }
90
+ .to(validated_one: ['is invalid'])
91
+ end
92
+ end
93
+
94
+ context do
95
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}], validated_one: {name: 'name'} }
96
+ it { is_expected.not_to be_valid }
97
+ specify do
98
+ expect { instance.validate }.to change { instance.errors.messages }
99
+ .to('validated_many.0.name': ["can't be blank"], validated_many: ['is invalid'])
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Validations::NestedValidator do
4
+ before do
5
+ stub_model(:validated_assoc) do
6
+ include Granite::Form::Model
7
+ include Granite::Form::Model::Lifecycle
8
+ include Granite::Form::Model::Primary
9
+
10
+ primary :id, Integer
11
+ attribute :name, String
12
+
13
+ validates_presence_of :name
14
+ end
15
+
16
+ stub_model(:unvalidated_assoc) do
17
+ include Granite::Form::Model
18
+ include Granite::Form::Model::Lifecycle
19
+
20
+ attribute :name, String
21
+ end
22
+
23
+ stub_model(:main) do
24
+ include Granite::Form::Model::Persistence
25
+ include Granite::Form::Model::Associations
26
+
27
+ attribute :name, String
28
+
29
+ validates_presence_of :name
30
+
31
+ embeds_one :validated_one, class_name: 'ValidatedAssoc'
32
+ embeds_one :unvalidated_one, class_name: 'UnvalidatedAssoc'
33
+ embeds_many :validated_many, class_name: 'ValidatedAssoc'
34
+ embeds_many :unvalidated_many, class_name: 'UnvalidatedAssoc'
35
+ end
36
+ end
37
+
38
+ context do
39
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {name: 'name'} }
40
+ it { is_expected.to be_valid }
41
+ end
42
+
43
+ context do
44
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {} }
45
+ it { is_expected.not_to be_valid }
46
+ specify do
47
+ expect { instance.validate }.to change { instance.errors.messages }
48
+ .to('validated_one.name': ["can't be blank"])
49
+ end
50
+ end
51
+
52
+ context do
53
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {name: 'name'} }
54
+ it { is_expected.to be_valid }
55
+ end
56
+
57
+ context do
58
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {} }
59
+ it { is_expected.to be_valid }
60
+ end
61
+
62
+ context do
63
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}] }
64
+ it { is_expected.to be_valid }
65
+ end
66
+
67
+ context do
68
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}] }
69
+ it { is_expected.not_to be_valid }
70
+ specify do
71
+ expect { instance.validate }.to change { instance.errors.messages }
72
+ .to('validated_many.0.name': ["can't be blank"])
73
+ end
74
+ end
75
+
76
+ context do
77
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{name: 'name'}] }
78
+ it { is_expected.to be_valid }
79
+ end
80
+
81
+ context do
82
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{}] }
83
+ it { is_expected.to be_valid }
84
+ end
85
+
86
+ context do
87
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}], validated_one: {} }
88
+ it { is_expected.not_to be_valid }
89
+ specify do
90
+ expect { instance.validate }.to change { instance.errors.messages }
91
+ .to('validated_one.name': ["can't be blank"])
92
+ end
93
+ end
94
+
95
+ context 'accepts nested attributes for one' do
96
+ before { Main.accepts_nested_attributes_for :validated_one, allow_destroy: true }
97
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {id: 1, name: 'name'} }
98
+
99
+ specify do
100
+ instance.validated_one_attributes = {id: 1, name: '', _destroy: true}
101
+ is_expected.to be_valid
102
+ end
103
+ end
104
+
105
+ context 'accepts nested attributes for many' do
106
+ before { Main.accepts_nested_attributes_for :validated_many, allow_destroy: true }
107
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{id: 1, name: 'name'}] }
108
+
109
+ specify do
110
+ instance.validated_many_attributes = [{id: 1, name: '', _destroy: true}]
111
+ is_expected.to be_valid
112
+ end
113
+ end
114
+
115
+ context 'object field is invalid and referenced object does not include AutosaveAssociation' do
116
+ before do
117
+ stub_class(:validated_object) do
118
+ include Granite::Form::Model::Validations
119
+ include Granite::Form::Model::Attributes
120
+ include Granite::Form::Model::Primary
121
+
122
+ primary :id
123
+ attribute :title, String
124
+ validates_presence_of :title
125
+ end
126
+
127
+ Main.class_eval do
128
+ include Granite::Form::Model::Associations
129
+ attribute :object, Object
130
+ validates :object, nested: true
131
+ end
132
+ end
133
+
134
+ subject(:instance) { Main.instantiate name: 'hello', object: object, validated_one: {name: 'name'} }
135
+
136
+ context do
137
+ let(:object) { ValidatedObject.new(title: 'Mr.') }
138
+ it { is_expected.to be_valid }
139
+ end
140
+
141
+ context do
142
+ let(:object) { ValidatedObject.new }
143
+ it do
144
+ expect { subject.valid? }.not_to raise_error
145
+ expect(subject).not_to be_valid
146
+ end
147
+ end
148
+ end
149
+
150
+ context do
151
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}], validated_one: {name: 'name'} }
152
+ it { is_expected.not_to be_valid }
153
+ specify do
154
+ expect { instance.validate }.to change { instance.errors.messages }
155
+ .to('validated_many.0.name': ["can't be blank"])
156
+ end
157
+ end
158
+
159
+ describe '.validates_nested?' do
160
+ specify { expect(Main).to be_validates_nested(:validated_one) }
161
+ specify { expect(Main).to be_validates_nested(:unvalidated_one) }
162
+ specify { expect(Main).not_to be_validates_nested(:something_else) }
163
+ end
164
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Validations do
4
+ let(:model) do
5
+ stub_model(:model) do
6
+ attribute :name, String
7
+ validates :name, presence: true
8
+ end
9
+ end
10
+
11
+ describe '#errors' do
12
+ specify { expect(model.new.errors).to be_a ActiveModel::Errors }
13
+ specify { expect(model.new.errors).to be_empty }
14
+ end
15
+
16
+ describe '#valid?' do
17
+ specify { expect(model.new).not_to be_valid }
18
+ specify { expect(model.new(name: 'Name')).to be_valid }
19
+ end
20
+
21
+ describe '#invalid?' do
22
+ specify { expect(model.new).to be_invalid }
23
+ specify { expect(model.new(name: 'Name')).not_to be_invalid }
24
+ end
25
+
26
+ describe '#validate!' do
27
+ specify { expect { model.new.validate! }.to raise_error Granite::Form::ValidationError }
28
+ specify { expect(model.new(name: 'Name').validate!).to eq(true) }
29
+ specify { expect { model.new(name: 'Name').validate! }.not_to raise_error }
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model do
4
+ let(:model) { stub_model }
5
+ specify { expect { model.blablabla }.to raise_error NoMethodError }
6
+
7
+ context 'Fault tolerance' do
8
+ specify { expect { model.new(foo: 'bar') }.not_to raise_error }
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form do
4
+ specify { expect(subject).to respond_to :include_root_in_json }
5
+ specify { expect(subject).to respond_to :include_root_in_json= }
6
+ specify { expect(subject).to respond_to :i18n_scope }
7
+ specify { expect(subject).to respond_to :i18n_scope= }
8
+ specify { expect(subject).to respond_to :primary_attribute }
9
+ specify { expect(subject).to respond_to :primary_attribute= }
10
+ specify { expect(subject).to respond_to :normalizer }
11
+ end