granite-form 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 (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