granite-form 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -2
  3. data/.github/workflows/{ci.yml → ruby.yml} +22 -4
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +3 -3
  6. data/Appraisals +1 -2
  7. data/CHANGELOG.md +15 -0
  8. data/README.md +6 -15
  9. data/docker-compose.yml +14 -0
  10. data/gemfiles/rails.5.0.gemfile +0 -1
  11. data/gemfiles/rails.5.1.gemfile +0 -1
  12. data/gemfiles/rails.5.2.gemfile +0 -1
  13. data/granite-form.gemspec +15 -15
  14. data/lib/granite/form/active_record/associations.rb +1 -1
  15. data/lib/granite/form/base.rb +1 -2
  16. data/lib/granite/form/errors.rb +0 -15
  17. data/lib/granite/form/model/associations/base.rb +0 -4
  18. data/lib/granite/form/model/associations/collection/embedded.rb +2 -1
  19. data/lib/granite/form/model/associations/collection/proxy.rb +1 -1
  20. data/lib/granite/form/model/associations/embeds_any.rb +7 -0
  21. data/lib/granite/form/model/associations/embeds_many.rb +9 -58
  22. data/lib/granite/form/model/associations/embeds_one.rb +7 -36
  23. data/lib/granite/form/model/associations/nested_attributes.rb +7 -7
  24. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +0 -4
  25. data/lib/granite/form/model/associations/persistence_adapters/base.rb +0 -4
  26. data/lib/granite/form/model/associations/references_many.rb +0 -32
  27. data/lib/granite/form/model/associations/references_one.rb +0 -28
  28. data/lib/granite/form/model/associations/reflections/embeds_any.rb +1 -1
  29. data/lib/granite/form/model/associations/reflections/embeds_many.rb +1 -1
  30. data/lib/granite/form/model/associations/reflections/embeds_one.rb +11 -1
  31. data/lib/granite/form/model/associations/reflections/references_any.rb +0 -4
  32. data/lib/granite/form/model/associations/reflections/singular.rb +0 -22
  33. data/lib/granite/form/model/associations.rb +0 -6
  34. data/lib/granite/form/model/attributes/attribute.rb +3 -21
  35. data/lib/granite/form/model/attributes/base.rb +5 -23
  36. data/lib/granite/form/model/attributes/reference_many.rb +1 -1
  37. data/lib/granite/form/model/attributes/reference_one.rb +1 -1
  38. data/lib/granite/form/model/attributes/reflections/attribute.rb +2 -8
  39. data/lib/granite/form/model/attributes/reflections/base/build_type_definition.rb +38 -0
  40. data/lib/granite/form/model/attributes/reflections/base.rb +20 -17
  41. data/lib/granite/form/model/attributes/reflections/collection/build_type_definition.rb +19 -0
  42. data/lib/granite/form/model/attributes/reflections/dictionary/build_type_definition.rb +19 -0
  43. data/lib/granite/form/model/attributes/reflections/dictionary.rb +0 -3
  44. data/lib/granite/form/model/attributes/reflections/reference_one.rb +0 -6
  45. data/lib/granite/form/model/attributes/reflections/represents/build_type_definition.rb +73 -0
  46. data/lib/granite/form/model/attributes/reflections/represents.rb +10 -2
  47. data/lib/granite/form/model/attributes/represents.rb +22 -37
  48. data/lib/granite/form/model/attributes.rb +10 -2
  49. data/lib/granite/form/model/persistence.rb +1 -19
  50. data/lib/granite/form/model/representation.rb +1 -0
  51. data/lib/granite/form/model/validations.rb +6 -0
  52. data/lib/granite/form/model.rb +1 -3
  53. data/lib/granite/form/types/active_support/time_zone.rb +2 -0
  54. data/lib/granite/form/types/array.rb +2 -0
  55. data/lib/granite/form/types/big_decimal.rb +2 -0
  56. data/lib/granite/form/types/boolean.rb +2 -0
  57. data/lib/granite/form/types/collection.rb +11 -0
  58. data/lib/granite/form/types/date.rb +2 -0
  59. data/lib/granite/form/types/date_time.rb +2 -0
  60. data/lib/granite/form/types/dictionary.rb +23 -0
  61. data/lib/granite/form/types/float.rb +2 -0
  62. data/lib/granite/form/types/has_subtype.rb +18 -0
  63. data/lib/granite/form/types/hash_with_action_controller_parameters.rb +2 -0
  64. data/lib/granite/form/types/integer.rb +2 -0
  65. data/lib/granite/form/types/object.rb +28 -0
  66. data/lib/granite/form/types/string.rb +2 -0
  67. data/lib/granite/form/types/time.rb +2 -0
  68. data/lib/granite/form/types/uuid.rb +2 -0
  69. data/lib/granite/form/types.rb +3 -0
  70. data/lib/granite/form/util.rb +55 -0
  71. data/lib/granite/form/version.rb +1 -1
  72. data/lib/granite/form.rb +1 -0
  73. data/spec/granite/form/active_record/associations_spec.rb +16 -18
  74. data/spec/granite/form/model/associations/embeds_many_spec.rb +29 -305
  75. data/spec/granite/form/model/associations/embeds_one_spec.rb +27 -212
  76. data/spec/granite/form/model/associations/nested_attributes_spec.rb +0 -95
  77. data/spec/granite/form/model/associations/references_many_spec.rb +5 -326
  78. data/spec/granite/form/model/associations/references_one_spec.rb +7 -279
  79. data/spec/granite/form/model/associations/reflections/embeds_any_spec.rb +1 -2
  80. data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +18 -26
  81. data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +16 -23
  82. data/spec/granite/form/model/associations/reflections/references_many_spec.rb +1 -1
  83. data/spec/granite/form/model/associations/reflections/references_one_spec.rb +1 -22
  84. data/spec/granite/form/model/associations/validations_spec.rb +0 -3
  85. data/spec/granite/form/model/associations_spec.rb +3 -24
  86. data/spec/granite/form/model/attributes/attribute_spec.rb +0 -29
  87. data/spec/granite/form/model/attributes/reflections/attribute_spec.rb +0 -9
  88. data/spec/granite/form/model/attributes/reflections/base/build_type_definition_spec.rb +27 -0
  89. data/spec/granite/form/model/attributes/reflections/base_spec.rb +16 -10
  90. data/spec/granite/form/model/attributes/reflections/collection/build_type_definition_spec.rb +24 -0
  91. data/spec/granite/form/model/attributes/reflections/dictionary/build_type_definition_spec.rb +24 -0
  92. data/spec/granite/form/model/attributes/reflections/dictionary_spec.rb +0 -6
  93. data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb +129 -0
  94. data/spec/granite/form/model/attributes/reflections/represents_spec.rb +43 -20
  95. data/spec/granite/form/model/attributes/represents_spec.rb +78 -55
  96. data/spec/granite/form/model/attributes_spec.rb +84 -23
  97. data/spec/granite/form/model/dirty_spec.rb +1 -7
  98. data/spec/granite/form/model/persistence_spec.rb +0 -2
  99. data/spec/granite/form/model/representation_spec.rb +4 -7
  100. data/spec/granite/form/model/validations/associated_spec.rb +2 -4
  101. data/spec/granite/form/model/validations/nested_spec.rb +2 -4
  102. data/spec/granite/form/model/validations_spec.rb +28 -1
  103. data/spec/granite/form/types/collection_spec.rb +22 -0
  104. data/spec/granite/form/types/dictionary_spec.rb +32 -0
  105. data/spec/granite/form/types/has_subtype_spec.rb +20 -0
  106. data/spec/granite/form/types/object_spec.rb +50 -4
  107. data/spec/granite/form/util_spec.rb +108 -0
  108. data/spec/spec_helper.rb +0 -15
  109. data/spec/support/active_record.rb +23 -0
  110. data/spec/support/shared/nested_attribute_examples.rb +3 -21
  111. metadata +56 -51
  112. data/.github/workflows/main.yml +0 -29
  113. data/gemfiles/rails.4.2.gemfile +0 -15
  114. data/lib/granite/form/model/attributes/collection.rb +0 -19
  115. data/lib/granite/form/model/attributes/dictionary.rb +0 -28
  116. data/lib/granite/form/model/attributes/localized.rb +0 -44
  117. data/lib/granite/form/model/attributes/reflections/localized.rb +0 -45
  118. data/lib/granite/form/model/callbacks.rb +0 -72
  119. data/lib/granite/form/model/lifecycle.rb +0 -309
  120. data/lib/granite/form/model/localization.rb +0 -26
  121. data/spec/granite/form/model/attributes/collection_spec.rb +0 -72
  122. data/spec/granite/form/model/attributes/dictionary_spec.rb +0 -100
  123. data/spec/granite/form/model/attributes/localized_spec.rb +0 -103
  124. data/spec/granite/form/model/attributes/reflections/localized_spec.rb +0 -37
  125. data/spec/granite/form/model/callbacks_spec.rb +0 -337
  126. data/spec/granite/form/model/lifecycle_spec.rb +0 -356
@@ -3,7 +3,8 @@ require 'spec_helper'
3
3
  describe Granite::Form::Model::Associations::Reflections::EmbedsMany do
4
4
  before do
5
5
  stub_model(:project) do
6
- include Granite::Form::Model::Lifecycle
6
+ include Granite::Form::Model::Persistence
7
+ include Granite::Form::Model::Associations
7
8
  attribute :title, String
8
9
  end
9
10
  stub_model(:user) do
@@ -24,24 +25,28 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsMany do
24
25
  attribute :name
25
26
  embeds_many :projects,
26
27
  read: lambda { |reflection, object|
27
- value = object.read_attribute(reflection.name)
28
+ value = object.instance_variable_get("@_value_#{reflection.name}")
28
29
  JSON.parse(value) if value.present?
29
30
  },
30
31
  write: lambda { |reflection, object, value|
31
- object.write_attribute(reflection.name, value.to_json)
32
+ value = value.to_json if value
33
+ object.instance_variable_set("@_value_#{reflection.name}", value)
32
34
  }
33
35
  end
34
36
  end
35
37
 
36
- let(:user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}].to_json }
38
+ let(:user) { User.new }
37
39
  let(:new_project1) { Project.new(title: 'Project 1') }
38
40
  let(:new_project2) { Project.new(title: 'Project 2') }
39
41
 
40
42
  specify do
41
- expect { user.projects.concat([new_project1, new_project2]) }
42
- .to change { user.read_attribute(:projects) }
43
- .from([{title: 'Genesis'}].to_json)
44
- .to([{title: 'Genesis'}, {title: 'Project 1'}, {title: 'Project 2'}].to_json)
43
+ expect do
44
+ user.projects = [new_project1, new_project2]
45
+ user.association(:projects).sync
46
+ end
47
+ .to change { user.projects(true) }
48
+ .from([])
49
+ .to([have_attributes(title: 'Project 1'), have_attributes(title: 'Project 2')])
45
50
  end
46
51
  end
47
52
 
@@ -54,23 +59,10 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsMany do
54
59
  specify { expect { user.projects.build(title: 'Project') }.to change { user.projects }.from([]).to([project]) }
55
60
  end
56
61
 
57
- describe '#create' do
58
- let(:project) { Project.new title: 'Project' }
59
- specify { expect(user.projects.create(title: 'Project')).to eq(project) }
60
- specify { expect { user.projects.create(title: 'Project') }.to change { user.projects }.from([]).to([project]) }
61
- end
62
-
63
- describe '#create!' do
64
- let(:project) { Project.new title: 'Project' }
65
- specify { expect(user.projects.create!(title: 'Project')).to eq(project) }
66
- specify { expect { user.projects.create!(title: 'Project') }.to change { user.projects }.from([]).to([project]) }
67
- end
68
-
69
62
  describe '#reload' do
70
63
  let(:project) { Project.new title: 'Project' }
71
64
  before do
72
- user.update(projects: [project])
73
- user.apply_association_changes!
65
+ user.write_attribute(:projects, [{title: 'Project'}])
74
66
  end
75
67
  before { user.projects.build }
76
68
 
@@ -120,8 +112,8 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsMany do
120
112
 
121
113
  specify { expect(User.reflect_on_association(:projects).klass).to eq(User::Project) }
122
114
  specify { expect(User.new.projects).to eq([]) }
123
- specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.projects).to be_a(Granite::Form::Model::Associations::Collection::Embedded) }
124
- specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.read_attribute(:projects)).to eq([{'title' => 'Project'}]) }
115
+ specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to be_a(Granite::Form::Model::Associations::Collection::Embedded) }
116
+ specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to match([have_attributes(title: 'Project')]) }
125
117
  end
126
118
 
127
119
  context do
@@ -138,8 +130,8 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsMany do
138
130
 
139
131
  specify { expect(User.reflect_on_association(:projects).klass).to eq(User::Project) }
140
132
  specify { expect(User.new.projects).to eq([]) }
141
- specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.projects).to be_a(Granite::Form::Model::Associations::Collection::Embedded) }
142
- specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.read_attribute(:projects)).to eq([{'title' => 'Project', 'value' => nil}]) }
133
+ specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to be_a(Granite::Form::Model::Associations::Collection::Embedded) }
134
+ specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to match([have_attributes(title: 'Project', value: nil)]) }
143
135
  end
144
136
  end
145
137
  end
@@ -3,7 +3,8 @@ require 'spec_helper'
3
3
  describe Granite::Form::Model::Associations::Reflections::EmbedsOne do
4
4
  before do
5
5
  stub_model(:author) do
6
- include Granite::Form::Model::Lifecycle
6
+ include Granite::Form::Model::Persistence
7
+ include Granite::Form::Model::Associations
7
8
  attribute :name, String
8
9
  end
9
10
 
@@ -27,22 +28,26 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsOne do
27
28
  attribute :title
28
29
  embeds_one :author,
29
30
  read: lambda { |reflection, object|
30
- value = object.read_attribute(reflection.name)
31
+ value = object.instance_variable_get("@_value_#{reflection.name}")
31
32
  JSON.parse(value) if value.present?
32
33
  },
33
34
  write: lambda { |reflection, object, value|
34
- object.write_attribute(reflection.name, value ? value.to_json : nil)
35
+ value = value.to_json if value
36
+ object.instance_variable_set("@_value_#{reflection.name}", value)
35
37
  }
36
38
  end
37
39
  end
38
40
 
39
- let(:book) { Book.instantiate author: {name: 'Duke'}.to_json }
41
+ let(:book) { Book.new }
40
42
  let(:author) { Author.new(name: 'Rick') }
41
43
 
42
44
  specify do
43
- expect { book.author = author }
44
- .to change { book.read_attribute(:author) }
45
- .from({name: 'Duke'}.to_json).to({name: 'Rick'}.to_json)
45
+ expect do
46
+ book.author = author
47
+ book.association(:author).sync
48
+ end
49
+ .to change { book.author(true) }
50
+ .from(nil).to(have_attributes(name: 'Rick'))
46
51
  end
47
52
  end
48
53
 
@@ -65,18 +70,6 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsOne do
65
70
  specify { expect { book.build_author(name: 'Author') }.to change { book.author }.from(nil).to(author) }
66
71
  end
67
72
 
68
- describe '#create_author' do
69
- let(:author) { Author.new name: 'Author' }
70
- specify { expect(book.create_author(name: 'Author')).to eq(author) }
71
- specify { expect { book.create_author(name: 'Author') }.to change { book.author }.from(nil).to(author) }
72
- end
73
-
74
- describe '#create_author!' do
75
- let(:author) { Author.new name: 'Author' }
76
- specify { expect(book.create_author!(name: 'Author')).to eq(author) }
77
- specify { expect { book.create_author!(name: 'Author') }.to change { book.author }.from(nil).to(author) }
78
- end
79
-
80
73
  context 'on the fly' do
81
74
  context do
82
75
  before do
@@ -92,8 +85,8 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsOne do
92
85
 
93
86
  specify { expect(Book.reflect_on_association(:author).klass).to eq(Book::Author) }
94
87
  specify { expect(Book.new.author).to be_nil }
95
- specify { expect(Book.new.tap { |b| b.create_author(name: 'Author') }.author).to be_a(Book::Author) }
96
- specify { expect(Book.new.tap { |b| b.create_author(name: 'Author') }.read_attribute(:author)).to eq('name' => 'Author') }
88
+ specify { expect(Book.new.tap { |b| b.build_author(name: 'Author') }.author).to be_a(Book::Author) }
89
+ specify { expect(Book.new.tap { |b| b.build_author(name: 'Author') }.author).to have_attributes(name: 'Author') }
97
90
  end
98
91
 
99
92
  context do
@@ -110,8 +103,8 @@ describe Granite::Form::Model::Associations::Reflections::EmbedsOne do
110
103
 
111
104
  specify { expect(Book.reflect_on_association(:author).klass).to eq(Book::Author) }
112
105
  specify { expect(Book.new.author).to be_nil }
113
- specify { expect(Book.new.tap { |b| b.create_author(name: 'Author') }.author).to be_a(Book::Author) }
114
- specify { expect(Book.new.tap { |b| b.create_author(name: 'Author') }.read_attribute(:author)).to eq('name' => 'Author', 'age' => nil) }
106
+ specify { expect(Book.new.tap { |b| b.build_author(name: 'Author') }.author).to be_a(Book::Author) }
107
+ specify { expect(Book.new.tap { |b| b.build_author(name: 'Author') }.author).to have_attributes(name: 'Author', age: nil) }
115
108
  end
116
109
  end
117
110
  end
@@ -4,7 +4,7 @@ describe Granite::Form::Model::Associations::Reflections::ReferencesMany do
4
4
  before do
5
5
  stub_class(:author, ActiveRecord::Base) do
6
6
  scope :name_starts_with_a, -> { name_starts_with('a') }
7
- scope :name_starts_with, ->(letter) { where("name LIKE '#{letter}%'") }
7
+ scope :name_starts_with, ->(letter) { where("name ILIKE '#{letter}%'") }
8
8
  end
9
9
 
10
10
  stub_model(:book) do
@@ -4,7 +4,7 @@ describe Granite::Form::Model::Associations::Reflections::ReferencesOne do
4
4
  before do
5
5
  stub_class(:author, ActiveRecord::Base) do
6
6
  scope :name_starts_with_a, -> { name_starts_with('a') }
7
- scope :name_starts_with, ->(letter) { where("name LIKE '#{letter}%'") }
7
+ scope :name_starts_with, ->(letter) { where("name ILIKE '#{letter}%'") }
8
8
  end
9
9
 
10
10
  stub_model(:book) do
@@ -263,25 +263,4 @@ describe Granite::Form::Model::Associations::Reflections::ReferencesOne do
263
263
  specify { expect { book.author_id = nil }.to change { book.author }.from(other).to(nil) }
264
264
  end
265
265
  end
266
-
267
- describe '#build_author' do
268
- specify { expect(book.build_author(name: 'Author')).to be_a(Author) }
269
- specify { expect(book.build_author(name: 'Author')).not_to be_persisted }
270
- specify { expect { book.build_author(name: 'Author') }.to change { book.author }.from(nil).to(an_instance_of(Author)) }
271
- specify { expect { book.build_author(name: 'Author') }.not_to change { book.author_id }.from(nil) }
272
- end
273
-
274
- describe '#create_author' do
275
- specify { expect(book.create_author(name: 'Author')).to be_a(Author) }
276
- specify { expect(book.create_author(name: 'Author')).to be_persisted }
277
- specify { expect { book.create_author(name: 'Author') }.to change { book.author }.from(nil).to(an_instance_of(Author)) }
278
- specify { expect { book.create_author(name: 'Author') }.to change { book.author_id }.from(nil).to(be_a(Integer)) }
279
- end
280
-
281
- describe '#create_author!' do
282
- specify { expect(book.create_author!(name: 'Author')).to be_a(Author) }
283
- specify { expect(book.create_author!(name: 'Author')).to be_persisted }
284
- specify { expect { book.create_author!(name: 'Author') }.to change { book.author }.from(nil).to(an_instance_of(Author)) }
285
- specify { expect { book.create_author!(name: 'Author') }.to change { book.author_id }.from(nil).to(be_a(Integer)) }
286
- end
287
266
  end
@@ -3,7 +3,6 @@ require 'spec_helper'
3
3
  describe Granite::Form::Model::Associations::Validations do
4
4
  before do
5
5
  stub_model(:project) do
6
- include Granite::Form::Model::Lifecycle
7
6
  include Granite::Form::Model::Associations
8
7
  include Granite::Form::Model::Associations::Validations
9
8
 
@@ -17,8 +16,6 @@ describe Granite::Form::Model::Associations::Validations do
17
16
  end
18
17
 
19
18
  stub_model(:profile) do
20
- include Granite::Form::Model::Lifecycle
21
-
22
19
  attribute :first_name, String
23
20
  attribute :last_name, String
24
21
  validates :first_name, presence: true
@@ -7,7 +7,7 @@ describe Granite::Form::Model::Associations do
7
7
  include Granite::Form::Model::Associations
8
8
  end
9
9
  stub_model(:project) do
10
- include Granite::Form::Model::Lifecycle
10
+ include Granite::Form::Model::Persistence
11
11
  end
12
12
  stub_model(:user, Nobody) do
13
13
  include Granite::Form::Model::Associations
@@ -65,7 +65,7 @@ describe Granite::Form::Model::Associations do
65
65
  context do
66
66
  before do
67
67
  stub_model(:project) do
68
- include Granite::Form::Model::Lifecycle
68
+ include Granite::Form::Model::Persistence
69
69
  include Granite::Form::Model::Associations
70
70
 
71
71
  attribute :title, String
@@ -80,7 +80,7 @@ describe Granite::Form::Model::Associations do
80
80
  end
81
81
 
82
82
  stub_model(:profile) do
83
- include Granite::Form::Model::Lifecycle
83
+ include Granite::Form::Model::Persistence
84
84
 
85
85
  attribute :first_name, String
86
86
  attribute :last_name, String
@@ -160,27 +160,6 @@ describe Granite::Form::Model::Associations do
160
160
  specify { expect(user.association_names).to eq(%i[profile projects]) }
161
161
  end
162
162
 
163
- describe '#apply_association_changes!' do
164
- let(:profile) { Profile.new first_name: 'Name' }
165
- let(:project) { Project.new title: 'Project' }
166
- let(:user) { User.new(profile: profile, projects: [project]) }
167
- before { project.build_author(name: 'Author') }
168
-
169
- specify do
170
- expect { user.apply_association_changes! }.to change { user.attributes['profile'] }
171
- .from(nil).to('first_name' => 'Name', 'last_name' => nil)
172
- end
173
- specify do
174
- expect { user.apply_association_changes! }.to change { user.attributes['projects'] }
175
- .from(nil).to([{'title' => 'Project', 'author' => {'name' => 'Author'}}])
176
- end
177
-
178
- context do
179
- let(:project) { Project.new }
180
- specify { expect { user.apply_association_changes! }.to raise_error Granite::Form::AssociationChangesNotApplied }
181
- end
182
- end
183
-
184
163
  describe '#instantiate' do
185
164
  before { User.send(:include, Granite::Form::Model::Persistence) }
186
165
  let(:profile) { Profile.new first_name: 'Name' }
@@ -46,8 +46,6 @@ describe Granite::Form::Model::Attributes::Attribute do
46
46
  specify { expect(attribute.default).to eq(nil) }
47
47
  specify { expect(attribute(default: 'hello').default).to eq('hello') }
48
48
  specify { expect(attribute(default: -> { value }).default).to eq(42) }
49
- specify { expect(attribute(default: ->(object) { object.value }).default).to eq(42) }
50
- specify { expect(attribute(default: ->(*args) { args.first.value }).default).to eq(42) }
51
49
  end
52
50
 
53
51
  describe '#defaultize' do
@@ -57,31 +55,6 @@ describe Granite::Form::Model::Attributes::Attribute do
57
55
  specify { expect(attribute(default: false, type: Boolean).defaultize(nil)).to eq(false) }
58
56
  end
59
57
 
60
- describe '#enum' do
61
- before { allow_any_instance_of(Dummy).to receive_messages(value: 1..5) }
62
-
63
- specify { expect(attribute.enum).to eq([].to_set) }
64
- specify { expect(attribute(enum: []).enum).to eq([].to_set) }
65
- specify { expect(attribute(enum: 'hello').enum).to eq(['hello'].to_set) }
66
- specify { expect(attribute(enum: %w[hello world]).enum).to eq(%w[hello world].to_set) }
67
- specify { expect(attribute(enum: [1..5]).enum).to eq([1..5].to_set) }
68
- specify { expect(attribute(enum: 1..5).enum).to eq((1..5).to_a.to_set) }
69
- specify { expect(attribute(enum: -> { 1..5 }).enum).to eq((1..5).to_a.to_set) }
70
- specify { expect(attribute(enum: -> { 'hello' }).enum).to eq(['hello'].to_set) }
71
- specify { expect(attribute(enum: -> { ['hello', 42] }).enum).to eq(['hello', 42].to_set) }
72
- specify { expect(attribute(enum: -> { value }).enum).to eq((1..5).to_a.to_set) }
73
- specify { expect(attribute(enum: ->(object) { object.value }).enum).to eq((1..5).to_a.to_set) }
74
- end
75
-
76
- describe '#enumerize' do
77
- specify { expect(attribute.enumerize('anything')).to eq('anything') }
78
- specify { expect(attribute(enum: ['hello', 42]).enumerize('hello')).to eq('hello') }
79
- specify { expect(attribute(enum: ['hello', 42]).enumerize('world')).to eq(nil) }
80
- specify { expect(attribute(enum: -> { 'hello' }).enumerize('hello')).to eq('hello') }
81
- specify { expect(attribute(enum: -> { 1..5 }).enumerize(2)).to eq(2) }
82
- specify { expect(attribute(enum: -> { 1..5 }).enumerize(42)).to eq(nil) }
83
- end
84
-
85
58
  describe '#normalize' do
86
59
  specify { expect(attribute.normalize(' hello ')).to eq(' hello ') }
87
60
  specify { expect(attribute(normalizer: ->(v) { v.strip }).normalize(' hello ')).to eq('hello') }
@@ -93,8 +66,6 @@ describe Granite::Form::Model::Attributes::Attribute do
93
66
  let(:other) { 'other' }
94
67
 
95
68
  specify { expect(attribute(normalizer: ->(_v) { value }).normalize(' hello ')).to eq('value') }
96
- specify { expect(attribute(normalizer: ->(_v, object) { object.value }).normalize(' hello ')).to eq('value') }
97
- specify { expect(attribute(normalizer: ->(_v, _object) { other }).normalize(' hello ')).to eq('other') }
98
69
  end
99
70
 
100
71
  context 'integration' do
@@ -43,15 +43,6 @@ describe Granite::Form::Model::Attributes::Reflections::Attribute do
43
43
  specify { expect(reflection(default: -> {}).defaultizer).to be_a Proc }
44
44
  end
45
45
 
46
- describe '#enumerizer' do
47
- specify { expect(reflection.enumerizer).to be_nil }
48
- specify { expect(reflection(enum: 42).enumerizer).to eq(42) }
49
- specify { expect(reflection(enum: -> {}).enumerizer).to be_a Proc }
50
- specify { expect(reflection(in: 42).enumerizer).to eq(42) }
51
- specify { expect(reflection(in: -> {}).enumerizer).to be_a Proc }
52
- specify { expect(reflection(enum: 42, in: -> {}).enumerizer).to eq(42) }
53
- end
54
-
55
46
  describe '#normalizers' do
56
47
  specify { expect(reflection.normalizers).to eq([]) }
57
48
  specify { expect(reflection(normalizer: -> {}).normalizers).to be_a Array }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Granite::Form::Model::Attributes::Reflections::Base::BuildTypeDefinition do
6
+ def build_type_definition(**options)
7
+ @reflection = Granite::Form::Model::Attributes::Reflections::Base.new(:field, options)
8
+ described_class.new(owner, @reflection).call
9
+ end
10
+
11
+ def have_type(type)
12
+ have_attributes(type: type, reflection: @reflection, owner: owner)
13
+ end
14
+
15
+ before do
16
+ stub_class :owner
17
+ stub_class(:dummy, String)
18
+ end
19
+
20
+ let(:owner) { Owner.new }
21
+
22
+ specify { expect { build_type_definition }.to raise_error('Type is not specified for `field`') }
23
+ specify { expect(build_type_definition(type: String)).to have_type(String) }
24
+ specify { expect(build_type_definition(type: :string)).to have_type(String) }
25
+ specify { expect(build_type_definition(type: Dummy)).to have_type(Dummy) }
26
+ specify { expect { build_type_definition(type: :blabla) }.to raise_error NameError }
27
+ end
@@ -30,6 +30,8 @@ describe Granite::Form::Model::Attributes::Reflections::Base do
30
30
  end
31
31
 
32
32
  describe '#build_attribute' do
33
+ subject { reflection.build_attribute(owner, nil) }
34
+
33
35
  before do
34
36
  stub_class('SomeScope::Borogoves', described_class)
35
37
  stub_class('Granite::Form::Model::Attributes::Borogoves', Granite::Form::Model::Attributes::Base)
@@ -39,18 +41,22 @@ describe Granite::Form::Model::Attributes::Reflections::Base do
39
41
  let(:reflection) { SomeScope::Borogoves.new(:field, type: Object) }
40
42
  let(:owner) { Owner.new }
41
43
 
42
- specify { expect(reflection.build_attribute(owner, nil)).to be_a(Granite::Form::Model::Attributes::Borogoves) }
43
- specify { expect(reflection.build_attribute(owner, nil).name).to eq('field') }
44
- specify { expect(reflection.build_attribute(owner, nil).owner).to eq(owner) }
44
+ it { is_expected.to be_a(Granite::Form::Model::Attributes::Borogoves) }
45
+ it { is_expected.to have_attributes(reflection: reflection, owner: owner, type: Object) }
45
46
  end
46
47
 
47
- describe '#type' do
48
- before { stub_class(:dummy, String) }
48
+ describe '#enum' do
49
+ specify { expect(reflection.enum).to be_nil }
50
+ specify { expect(reflection(enum: 42).enum).to eq(42) }
51
+ specify { expect(reflection(enum: -> {}).enum).to be_a Proc }
52
+ specify { expect(reflection(in: 42).enum).to eq(42) }
53
+ specify { expect(reflection(in: -> {}).enum).to be_a Proc }
54
+ specify { expect(reflection(enum: 42, in: -> {}).enum).to eq(42) }
55
+ end
49
56
 
50
- specify { expect { reflection.type }.to raise_error('Type is not specified for `field`') }
51
- specify { expect(reflection(type: String).type).to eq(String) }
52
- specify { expect(reflection(type: :string).type).to eq(String) }
53
- specify { expect(reflection(type: Dummy).type).to eq(Dummy) }
54
- specify { expect { reflection(type: :blabla).type }.to raise_error NameError }
57
+ describe '#keys' do
58
+ specify { expect(reflection.keys).to eq([]) }
59
+ specify { expect(reflection(keys: ['a', :b]).keys).to eq(%w[a b]) }
60
+ specify { expect(reflection(keys: :c).keys).to eq(%w[c]) }
55
61
  end
56
62
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Granite::Form::Model::Attributes::Reflections::Collection::BuildTypeDefinition do
6
+ def build_type_definition(name = :name, **options)
7
+ @reflection = Granite::Form::Model::Attributes::Reflections::Collection.new(name, options)
8
+ described_class.new(owner, @reflection).call
9
+ end
10
+
11
+ def have_collection_type(type)
12
+ subtype_definition = have_attributes(type: type, reflection: @reflection, owner: owner)
13
+ have_attributes(subtype_definition: subtype_definition)
14
+ end
15
+
16
+ before do
17
+ stub_class :owner
18
+ stub_class(:dummy, String)
19
+ end
20
+
21
+ let(:owner) { Owner.new }
22
+
23
+ it { expect(build_type_definition(type: String)).to have_collection_type(String) }
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Granite::Form::Model::Attributes::Reflections::Dictionary::BuildTypeDefinition do
6
+ def build_type_definition(name = :name, **options)
7
+ @reflection = Granite::Form::Model::Attributes::Reflections::Dictionary.new(name, options)
8
+ described_class.new(owner, @reflection).call
9
+ end
10
+
11
+ def have_dictionary_type(type)
12
+ subtype_definition = have_attributes(type: type, reflection: @reflection, owner: owner)
13
+ have_attributes(subtype_definition: subtype_definition)
14
+ end
15
+
16
+ before do
17
+ stub_class :owner
18
+ stub_class(:dummy, String)
19
+ end
20
+
21
+ let(:owner) { Owner.new }
22
+
23
+ it { expect(build_type_definition(type: String)).to have_dictionary_type(String) }
24
+ end
@@ -34,10 +34,4 @@ describe Granite::Form::Model::Attributes::Reflections::Dictionary do
34
34
  expect(Target).to be_method_defined(:field_alias_values)
35
35
  end
36
36
  end
37
-
38
- describe '#keys' do
39
- specify { expect(reflection.keys).to eq([]) }
40
- specify { expect(reflection(keys: ['a', :b]).keys).to eq(%w[a b]) }
41
- specify { expect(reflection(keys: :c).keys).to eq(%w[c]) }
42
- end
43
37
  end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Granite::Form::Model::Attributes::Reflections::Represents::BuildTypeDefinition do
6
+ def build_type_definition(name = :name, **options)
7
+ @reflection = Granite::Form::Model::Attributes::Reflections::Represents.new(name, options.reverse_merge(of: :author))
8
+ described_class.new(owner, @reflection).call
9
+ end
10
+
11
+ def have_type(type)
12
+ have_attributes(type: type, reflection: @reflection, owner: owner)
13
+ end
14
+
15
+ let(:owner) { Owner.new }
16
+
17
+ before do
18
+ stub_model :author
19
+ stub_model(:owner) do
20
+ def author
21
+ @author ||= Author.new
22
+ end
23
+ end
24
+ end
25
+
26
+ it { expect(build_type_definition).to have_type(Object) }
27
+ it { expect(build_type_definition(type: String)).to have_type(String) }
28
+
29
+ context 'when defined in attribute' do
30
+ before { Author.attribute :name, String }
31
+
32
+ it { expect(build_type_definition).to have_type(String) }
33
+ it { expect(build_type_definition(type: Integer)).to have_type(Integer) }
34
+ end
35
+
36
+ context 'when defined in represented attribute' do
37
+ before do
38
+ stub_model(:real_author) do
39
+ attribute :name, Boolean
40
+ end
41
+ Author.class_eval do
42
+ include Granite::Form::Model::Representation
43
+ represents :name, of: :subject
44
+
45
+ def subject
46
+ @subject ||= RealAuthor.new
47
+ end
48
+ end
49
+ end
50
+
51
+ it { expect(build_type_definition).to have_type(Boolean) }
52
+ end
53
+
54
+ context 'when defined in references_many' do
55
+ before do
56
+ stub_class(:user, ActiveRecord::Base)
57
+ Author.class_eval do
58
+ include Granite::Form::Model::Associations
59
+ references_many :users
60
+ end
61
+ end
62
+
63
+ it do
64
+ attribute = build_type_definition(:user_ids)
65
+ expect(attribute).to be_a(Granite::Form::Types::Collection)
66
+ expect(attribute.subtype_definition).to have_type(Integer)
67
+ end
68
+ end
69
+
70
+ context 'when defined in collection' do
71
+ before do
72
+ Author.collection :users, String
73
+ end
74
+
75
+ it do
76
+ attribute = build_type_definition(:users)
77
+ expect(attribute).to be_a(Granite::Form::Types::Collection)
78
+ expect(attribute.subtype_definition).to have_type(String)
79
+ end
80
+ end
81
+
82
+ context 'when defined in dictionary' do
83
+ before do
84
+ Author.dictionary :numbers, Float
85
+ end
86
+
87
+ it do
88
+ attribute = build_type_definition(:numbers)
89
+ expect(attribute).to be_a(Granite::Form::Types::Dictionary)
90
+ expect(attribute.subtype_definition).to have_type(Float)
91
+ end
92
+ end
93
+
94
+ context 'when defined in ActiveRecord::Base' do
95
+ before do
96
+ stub_class(:author, ActiveRecord::Base) do
97
+ alias_attribute :full_name, :name
98
+ end
99
+ end
100
+
101
+ it { expect(build_type_definition).to have_type(String) }
102
+ it { expect(build_type_definition(:status)).to have_type(Integer) }
103
+ it { expect(build_type_definition(:full_name)).to have_type(String) }
104
+ it { expect(build_type_definition(:unknown_attribute)).to have_type(Object) }
105
+ it do
106
+ attribute = build_type_definition(:related_ids)
107
+ expect(attribute).to be_a(Granite::Form::Types::Collection)
108
+ expect(attribute.subtype_definition).to have_type(Integer)
109
+ end
110
+
111
+ context 'with enum' do
112
+ before do
113
+ if ActiveRecord.gem_version >= Gem::Version.new('7.0')
114
+ Author.enum :status, once: 1, many: 2
115
+ else
116
+ Author.enum status: %i[once many]
117
+ end
118
+ end
119
+
120
+ it { expect(build_type_definition(:status)).to have_type(String) }
121
+ end
122
+
123
+ context 'with serialized attribute' do
124
+ before { Author.serialize :data }
125
+
126
+ it { expect(build_type_definition(:data)).to have_type(Object) }
127
+ end
128
+ end
129
+ end