blanks 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +427 -0
- data/Rakefile +8 -0
- data/examples/advanced_features_example.rb +81 -0
- data/examples/assign_from_model_example.rb +54 -0
- data/examples/model_name_example.rb +31 -0
- data/examples/normalization_example.rb +39 -0
- data/examples/post_form_example.rb +52 -0
- data/lib/blanks/association_proxy.rb +92 -0
- data/lib/blanks/associations.rb +58 -0
- data/lib/blanks/base.rb +256 -0
- data/lib/blanks/model_naming.rb +24 -0
- data/lib/blanks/nested_attributes.rb +131 -0
- data/lib/blanks/normalization.rb +38 -0
- data/lib/blanks/version.rb +3 -0
- data/lib/blanks.rb +18 -0
- data/spec/blanks/association_proxy_spec.rb +214 -0
- data/spec/blanks/associations_spec.rb +185 -0
- data/spec/blanks/attributes_extraction_spec.rb +138 -0
- data/spec/blanks/base_spec.rb +361 -0
- data/spec/blanks/callbacks_spec.rb +60 -0
- data/spec/blanks/custom_primary_key_spec.rb +168 -0
- data/spec/blanks/dirty_tracking_spec.rb +61 -0
- data/spec/blanks/i18n_spec.rb +33 -0
- data/spec/blanks/id_tracking_spec.rb +168 -0
- data/spec/blanks/inherit_attributes_from_spec.rb +148 -0
- data/spec/blanks/inherit_validations_from_spec.rb +260 -0
- data/spec/blanks/model_naming_spec.rb +82 -0
- data/spec/blanks/nested_attributes_spec.rb +378 -0
- data/spec/blanks/normalization_spec.rb +122 -0
- data/spec/dummy/Gemfile +10 -0
- data/spec/dummy/Gemfile.lock +242 -0
- data/spec/dummy/Rakefile +5 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/simple_form/articles_controller.rb +65 -0
- data/spec/dummy/app/controllers/simple_form/posts_controller.rb +59 -0
- data/spec/dummy/app/controllers/standard/articles_controller.rb +65 -0
- data/spec/dummy/app/controllers/standard/posts_controller.rb +59 -0
- data/spec/dummy/app/forms/article_form.rb +17 -0
- data/spec/dummy/app/forms/cover_image_form.rb +9 -0
- data/spec/dummy/app/forms/post_form.rb +10 -0
- data/spec/dummy/app/forms/tag_form.rb +12 -0
- data/spec/dummy/app/models/application_record.rb +5 -0
- data/spec/dummy/app/models/article.rb +11 -0
- data/spec/dummy/app/models/cover_image.rb +7 -0
- data/spec/dummy/app/models/post.rb +8 -0
- data/spec/dummy/app/models/tag.rb +7 -0
- data/spec/dummy/app/views/layouts/application.html.erb +43 -0
- data/spec/dummy/app/views/simple_form/articles/_form.html.erb +43 -0
- data/spec/dummy/app/views/simple_form/articles/edit.html.erb +5 -0
- data/spec/dummy/app/views/simple_form/articles/index.html.erb +29 -0
- data/spec/dummy/app/views/simple_form/articles/new.html.erb +5 -0
- data/spec/dummy/app/views/simple_form/articles/show.html.erb +29 -0
- data/spec/dummy/app/views/simple_form/posts/_form.html.erb +19 -0
- data/spec/dummy/app/views/simple_form/posts/edit.html.erb +5 -0
- data/spec/dummy/app/views/simple_form/posts/index.html.erb +27 -0
- data/spec/dummy/app/views/simple_form/posts/new.html.erb +5 -0
- data/spec/dummy/app/views/simple_form/posts/show.html.erb +12 -0
- data/spec/dummy/app/views/standard/articles/_form.html.erb +61 -0
- data/spec/dummy/app/views/standard/articles/edit.html.erb +5 -0
- data/spec/dummy/app/views/standard/articles/index.html.erb +29 -0
- data/spec/dummy/app/views/standard/articles/new.html.erb +5 -0
- data/spec/dummy/app/views/standard/articles/show.html.erb +29 -0
- data/spec/dummy/app/views/standard/posts/_form.html.erb +30 -0
- data/spec/dummy/app/views/standard/posts/edit.html.erb +5 -0
- data/spec/dummy/app/views/standard/posts/index.html.erb +27 -0
- data/spec/dummy/app/views/standard/posts/new.html.erb +5 -0
- data/spec/dummy/app/views/standard/posts/show.html.erb +12 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/config/application.rb +18 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +12 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +9 -0
- data/spec/dummy/config/environments/test.rb +8 -0
- data/spec/dummy/config/initializers/simple_form.rb +21 -0
- data/spec/dummy/config/routes.rb +15 -0
- data/spec/dummy/config/storage.yml +3 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/db/migrate/1_create_posts.rb +12 -0
- data/spec/dummy/db/migrate/2_create_articles.rb +12 -0
- data/spec/dummy/db/migrate/3_create_cover_images.rb +12 -0
- data/spec/dummy/db/migrate/4_create_tags.rb +12 -0
- data/spec/dummy/db/migrate/5_create_active_storage_tables.rb +36 -0
- data/spec/dummy/db/schema.rb +82 -0
- data/spec/dummy/spec/examples.txt +145 -0
- data/spec/dummy/tmp/local_secret.txt +1 -0
- data/spec/examples.txt +157 -0
- data/spec/spec_helper.rb +21 -0
- metadata +159 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "ID tracking in nested forms" do
|
|
6
|
+
describe "AssociationProxy#find_by_id" do
|
|
7
|
+
it "finds form by id" do
|
|
8
|
+
form_class = Class.new(Blanks::Base) do
|
|
9
|
+
attribute :id, :integer
|
|
10
|
+
attribute :url, :string
|
|
11
|
+
end
|
|
12
|
+
stub_const("ImageForm", form_class)
|
|
13
|
+
|
|
14
|
+
proxy = Blanks::AssociationProxy.new("ImageForm")
|
|
15
|
+
image = proxy.new(id: 123, url: "test.jpg")
|
|
16
|
+
|
|
17
|
+
expect(proxy.find_by_id(123)).to eq(image)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "returns nil when id not found" do
|
|
21
|
+
form_class = Class.new(Blanks::Base) do
|
|
22
|
+
attribute :id, :integer
|
|
23
|
+
attribute :url, :string
|
|
24
|
+
end
|
|
25
|
+
stub_const("ImageForm", form_class)
|
|
26
|
+
|
|
27
|
+
proxy = Blanks::AssociationProxy.new("ImageForm")
|
|
28
|
+
|
|
29
|
+
expect(proxy.find_by_id(999)).to be_nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "handles string and integer ids" do
|
|
33
|
+
form_class = Class.new(Blanks::Base) do
|
|
34
|
+
attribute :id, :integer
|
|
35
|
+
attribute :url, :string
|
|
36
|
+
end
|
|
37
|
+
stub_const("ImageForm", form_class)
|
|
38
|
+
|
|
39
|
+
proxy = Blanks::AssociationProxy.new("ImageForm")
|
|
40
|
+
image = proxy.new(id: 123, url: "test.jpg")
|
|
41
|
+
|
|
42
|
+
expect(proxy.find_by_id("123")).to eq(image)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "updating existing nested forms by id" do
|
|
47
|
+
it "updates existing form when id matches" do
|
|
48
|
+
image_form_class = Class.new(Blanks::Base) do
|
|
49
|
+
attribute :id, :integer
|
|
50
|
+
attribute :url, :string
|
|
51
|
+
end
|
|
52
|
+
stub_const("ImageForm", image_form_class)
|
|
53
|
+
|
|
54
|
+
form_class = Class.new(Blanks::Base) do
|
|
55
|
+
has_many :images
|
|
56
|
+
attribute :title, :string
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
form = form_class.new(title: "test")
|
|
60
|
+
form.images.new(id: 1, url: "original.jpg")
|
|
61
|
+
form.images.new(id: 2, url: "another.jpg")
|
|
62
|
+
|
|
63
|
+
form.images_attributes = [
|
|
64
|
+
{ id: 1, url: "updated.jpg" }
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
expect(form.images[0].url).to eq("updated.jpg")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "creates new form when id not found" do
|
|
71
|
+
image_form_class = Class.new(Blanks::Base) do
|
|
72
|
+
attribute :id, :integer
|
|
73
|
+
attribute :url, :string
|
|
74
|
+
end
|
|
75
|
+
stub_const("ImageForm", image_form_class)
|
|
76
|
+
|
|
77
|
+
form_class = Class.new(Blanks::Base) do
|
|
78
|
+
has_many :images
|
|
79
|
+
attribute :title, :string
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
form = form_class.new(title: "test")
|
|
83
|
+
form.images.new(id: 1, url: "original.jpg")
|
|
84
|
+
|
|
85
|
+
form.images_attributes = [
|
|
86
|
+
{ id: 999, url: "new.jpg" }
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
expect(form.images.count).to eq(2)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "creates new form when no id provided" do
|
|
93
|
+
image_form_class = Class.new(Blanks::Base) do
|
|
94
|
+
attribute :id, :integer
|
|
95
|
+
attribute :url, :string
|
|
96
|
+
end
|
|
97
|
+
stub_const("ImageForm", image_form_class)
|
|
98
|
+
|
|
99
|
+
form_class = Class.new(Blanks::Base) do
|
|
100
|
+
has_many :images
|
|
101
|
+
attribute :title, :string
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
form = form_class.new(title: "test")
|
|
105
|
+
form.images.new(id: 1, url: "original.jpg")
|
|
106
|
+
|
|
107
|
+
form.images_attributes = [
|
|
108
|
+
{ url: "new.jpg" }
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
expect(form.images.count).to eq(2)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "preserves unchanged forms" do
|
|
115
|
+
image_form_class = Class.new(Blanks::Base) do
|
|
116
|
+
attribute :id, :integer
|
|
117
|
+
attribute :url, :string
|
|
118
|
+
end
|
|
119
|
+
stub_const("ImageForm", image_form_class)
|
|
120
|
+
|
|
121
|
+
form_class = Class.new(Blanks::Base) do
|
|
122
|
+
has_many :images
|
|
123
|
+
attribute :title, :string
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
form = form_class.new(title: "test")
|
|
127
|
+
form.images.new(id: 1, url: "first.jpg")
|
|
128
|
+
form.images.new(id: 2, url: "second.jpg")
|
|
129
|
+
|
|
130
|
+
form.images_attributes = [
|
|
131
|
+
{ id: 1, url: "updated.jpg" }
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
expect(form.images.count).to eq(2)
|
|
135
|
+
expect(form.images[0].url).to eq("updated.jpg")
|
|
136
|
+
expect(form.images[1].url).to eq("second.jpg")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "works with from_model then update" do
|
|
140
|
+
model_image1 = Struct.new(:id, :url).new(1, "model1.jpg")
|
|
141
|
+
model_image2 = Struct.new(:id, :url).new(2, "model2.jpg")
|
|
142
|
+
model = Struct.new(:title, :images).new("test", [model_image1, model_image2])
|
|
143
|
+
|
|
144
|
+
image_form_class = Class.new(Blanks::Base) do
|
|
145
|
+
attribute :id, :integer
|
|
146
|
+
attribute :url, :string
|
|
147
|
+
end
|
|
148
|
+
stub_const("ImageForm", image_form_class)
|
|
149
|
+
|
|
150
|
+
form_class = Class.new(Blanks::Base) do
|
|
151
|
+
has_many :images
|
|
152
|
+
attribute :title, :string
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
form = form_class.from_model(model)
|
|
156
|
+
|
|
157
|
+
form.images_attributes = [
|
|
158
|
+
{ id: 1, url: "updated1.jpg" },
|
|
159
|
+
{ url: "new.jpg" }
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
expect(form.images.count).to eq(3)
|
|
163
|
+
expect(form.images[0].url).to eq("updated1.jpg")
|
|
164
|
+
expect(form.images[1].url).to eq("model2.jpg")
|
|
165
|
+
expect(form.images[2].url).to eq("new.jpg")
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Blanks::Base.inherit_attributes_from" do
|
|
6
|
+
before(:all) do
|
|
7
|
+
@model_class = Class.new do
|
|
8
|
+
include ActiveModel::Model
|
|
9
|
+
include ActiveModel::Attributes
|
|
10
|
+
|
|
11
|
+
attribute :id, :integer
|
|
12
|
+
attribute :title, :string
|
|
13
|
+
attribute :content, :string
|
|
14
|
+
attribute :published_at, :datetime
|
|
15
|
+
attribute :view_count, :integer
|
|
16
|
+
attribute :created_at, :datetime
|
|
17
|
+
attribute :updated_at, :datetime
|
|
18
|
+
|
|
19
|
+
def self.name
|
|
20
|
+
"Post"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "with no options" do
|
|
26
|
+
it "inherits all attributes from model class" do
|
|
27
|
+
model_class = @model_class
|
|
28
|
+
form_class = Class.new(Blanks::Base) do
|
|
29
|
+
inherit_attributes_from model_class
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
expect(form_class.attribute_names).to include("id", "title", "content", "published_at", "view_count", "created_at", "updated_at")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "preserves attribute types from model class" do
|
|
36
|
+
model_class = @model_class
|
|
37
|
+
form_class = Class.new(Blanks::Base) do
|
|
38
|
+
inherit_attributes_from model_class
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
form = form_class.new(title: "test", view_count: "42")
|
|
42
|
+
|
|
43
|
+
expect(form.title).to eq("test")
|
|
44
|
+
expect(form.view_count).to eq(42)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "with only: option" do
|
|
49
|
+
it "inherits only specified attributes" do
|
|
50
|
+
model_class = @model_class
|
|
51
|
+
form_class = Class.new(Blanks::Base) do
|
|
52
|
+
inherit_attributes_from model_class, only: [:title, :content]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
expect(form_class.attribute_names).to include("title", "content")
|
|
56
|
+
expect(form_class.attribute_names).not_to include("id", "created_at", "updated_at")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "works with single attribute" do
|
|
60
|
+
model_class = @model_class
|
|
61
|
+
form_class = Class.new(Blanks::Base) do
|
|
62
|
+
inherit_attributes_from model_class, only: :title
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
expect(form_class.attribute_names).to eq(["title"])
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe "with except: option" do
|
|
70
|
+
it "inherits all attributes except specified ones" do
|
|
71
|
+
model_class = @model_class
|
|
72
|
+
form_class = Class.new(Blanks::Base) do
|
|
73
|
+
inherit_attributes_from model_class, except: [:created_at, :updated_at]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
expect(form_class.attribute_names).to include("id", "title", "content", "published_at", "view_count")
|
|
77
|
+
expect(form_class.attribute_names).not_to include("created_at", "updated_at")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "works with single attribute" do
|
|
81
|
+
model_class = @model_class
|
|
82
|
+
form_class = Class.new(Blanks::Base) do
|
|
83
|
+
inherit_attributes_from model_class, except: :id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
expect(form_class.attribute_names).to include("title", "content")
|
|
87
|
+
expect(form_class.attribute_names).not_to include("id")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe "validation" do
|
|
92
|
+
it "raises error when both only and except are specified" do
|
|
93
|
+
model_class = @model_class
|
|
94
|
+
expect {
|
|
95
|
+
Class.new(Blanks::Base) do
|
|
96
|
+
inherit_attributes_from model_class, only: [:title], except: [:id]
|
|
97
|
+
end
|
|
98
|
+
}.to raise_error(ArgumentError, "cannot specify both :only and :except")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "raises error when model class does not respond to attribute_types or columns" do
|
|
102
|
+
invalid_class = Class.new
|
|
103
|
+
|
|
104
|
+
expect {
|
|
105
|
+
Class.new(Blanks::Base) do
|
|
106
|
+
inherit_attributes_from invalid_class
|
|
107
|
+
end
|
|
108
|
+
}.to raise_error(ArgumentError, /does not respond to :attribute_types or :columns/)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe "integration with existing attributes" do
|
|
113
|
+
it "does not override existing attribute definitions" do
|
|
114
|
+
model_class = @model_class
|
|
115
|
+
form_class = Class.new(Blanks::Base) do
|
|
116
|
+
attribute :title, :integer
|
|
117
|
+
inherit_attributes_from model_class, only: [:title, :content]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
expect(form_class.attribute_types["title"].type).to eq(:integer)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "adds new attributes alongside existing ones" do
|
|
124
|
+
model_class = @model_class
|
|
125
|
+
form_class = Class.new(Blanks::Base) do
|
|
126
|
+
attribute :custom_field, :string
|
|
127
|
+
inherit_attributes_from model_class, only: [:title, :content]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
expect(form_class.attribute_names).to include("custom_field", "title", "content")
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe "integration with from_model" do
|
|
135
|
+
it "loads values from model for inherited attributes" do
|
|
136
|
+
model_class = @model_class
|
|
137
|
+
form_class = Class.new(Blanks::Base) do
|
|
138
|
+
inherit_attributes_from model_class, only: [:title, :content]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
model = model_class.new(title: "Test Post", content: "Test Content")
|
|
142
|
+
form = form_class.from_model(model)
|
|
143
|
+
|
|
144
|
+
expect(form.title).to eq("Test Post")
|
|
145
|
+
expect(form.content).to eq("Test Content")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Blanks::Base.inherit_validations_from" do
|
|
6
|
+
before(:all) do
|
|
7
|
+
@model_class = Class.new do
|
|
8
|
+
include ActiveModel::Model
|
|
9
|
+
include ActiveModel::Attributes
|
|
10
|
+
include ActiveModel::Validations
|
|
11
|
+
|
|
12
|
+
attribute :id, :integer
|
|
13
|
+
attribute :title, :string
|
|
14
|
+
attribute :content, :string
|
|
15
|
+
attribute :email, :string
|
|
16
|
+
|
|
17
|
+
validates :title, presence: true
|
|
18
|
+
validates :content, presence: true, length: { minimum: 10 }
|
|
19
|
+
validates :email, format: { with: /\A[^@\s]+@[^@\s]+\z/ }
|
|
20
|
+
|
|
21
|
+
def self.name
|
|
22
|
+
"Post"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@model_with_conditionals = Class.new do
|
|
27
|
+
include ActiveModel::Model
|
|
28
|
+
include ActiveModel::Attributes
|
|
29
|
+
include ActiveModel::Validations
|
|
30
|
+
|
|
31
|
+
attribute :title, :string
|
|
32
|
+
attribute :content, :string
|
|
33
|
+
attribute :status, :string
|
|
34
|
+
|
|
35
|
+
validates :title, presence: true
|
|
36
|
+
validates :content, presence: true, if: -> { status == "published" }
|
|
37
|
+
validates :status, presence: true, if: :published?
|
|
38
|
+
|
|
39
|
+
def published?
|
|
40
|
+
status == "published"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.name
|
|
44
|
+
"ConditionalPost"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "with no options" do
|
|
50
|
+
it "inherits all validators from model class" do
|
|
51
|
+
model_class = @model_class
|
|
52
|
+
form_class = Class.new(Blanks::Base) do
|
|
53
|
+
model_name_for :test
|
|
54
|
+
attribute :title, :string
|
|
55
|
+
attribute :content, :string
|
|
56
|
+
attribute :email, :string
|
|
57
|
+
inherit_validations_from model_class
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
form = form_class.new
|
|
61
|
+
form.valid?
|
|
62
|
+
|
|
63
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
64
|
+
expect(form.errors[:content]).to include("can't be blank")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "preserves validator options" do
|
|
68
|
+
model_class = @model_class
|
|
69
|
+
form_class = Class.new(Blanks::Base) do
|
|
70
|
+
model_name_for :test
|
|
71
|
+
attribute :title, :string
|
|
72
|
+
attribute :content, :string
|
|
73
|
+
inherit_validations_from model_class, only: [:content]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
form = form_class.new(content: "short")
|
|
77
|
+
form.valid?
|
|
78
|
+
|
|
79
|
+
expect(form.errors[:content]).to include("is too short (minimum is 10 characters)")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "inherits format validators" do
|
|
83
|
+
model_class = @model_class
|
|
84
|
+
form_class = Class.new(Blanks::Base) do
|
|
85
|
+
model_name_for :test
|
|
86
|
+
attribute :email, :string
|
|
87
|
+
inherit_validations_from model_class, only: [:email]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
form = form_class.new(email: "invalid")
|
|
91
|
+
form.valid?
|
|
92
|
+
|
|
93
|
+
expect(form.errors[:email]).to include("is invalid")
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "with only: option" do
|
|
98
|
+
it "inherits only specified validators" do
|
|
99
|
+
model_class = @model_class
|
|
100
|
+
form_class = Class.new(Blanks::Base) do
|
|
101
|
+
model_name_for :test
|
|
102
|
+
attribute :title, :string
|
|
103
|
+
attribute :content, :string
|
|
104
|
+
inherit_validations_from model_class, only: [:title]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
form = form_class.new
|
|
108
|
+
form.valid?
|
|
109
|
+
|
|
110
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
111
|
+
expect(form.errors[:content]).to be_empty
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "works with single attribute" do
|
|
115
|
+
model_class = @model_class
|
|
116
|
+
form_class = Class.new(Blanks::Base) do
|
|
117
|
+
model_name_for :test
|
|
118
|
+
attribute :title, :string
|
|
119
|
+
inherit_validations_from model_class, only: :title
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
form = form_class.new
|
|
123
|
+
form.valid?
|
|
124
|
+
|
|
125
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe "with except: option" do
|
|
130
|
+
it "inherits all validators except specified ones" do
|
|
131
|
+
model_class = @model_class
|
|
132
|
+
form_class = Class.new(Blanks::Base) do
|
|
133
|
+
model_name_for :test
|
|
134
|
+
attribute :title, :string
|
|
135
|
+
attribute :content, :string
|
|
136
|
+
attribute :email, :string
|
|
137
|
+
inherit_validations_from model_class, except: [:content, :email]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
form = form_class.new
|
|
141
|
+
form.valid?
|
|
142
|
+
|
|
143
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
144
|
+
expect(form.errors[:content]).to be_empty
|
|
145
|
+
expect(form.errors[:email]).to be_empty
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "works with single attribute" do
|
|
149
|
+
model_class = @model_class
|
|
150
|
+
form_class = Class.new(Blanks::Base) do
|
|
151
|
+
model_name_for :test
|
|
152
|
+
attribute :title, :string
|
|
153
|
+
attribute :content, :string
|
|
154
|
+
inherit_validations_from model_class, except: [:content, :email]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
form = form_class.new
|
|
158
|
+
form.valid?
|
|
159
|
+
|
|
160
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
161
|
+
expect(form.errors[:content]).to be_empty
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe "validation" do
|
|
166
|
+
it "raises error when both only and except are specified" do
|
|
167
|
+
model_class = @model_class
|
|
168
|
+
expect {
|
|
169
|
+
Class.new(Blanks::Base) do
|
|
170
|
+
inherit_validations_from model_class, only: [:title], except: [:content]
|
|
171
|
+
end
|
|
172
|
+
}.to raise_error(ArgumentError, "cannot specify both :only and :except")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe "skipping unsafe validators" do
|
|
177
|
+
it "skips validators with proc conditionals" do
|
|
178
|
+
model_class = @model_with_conditionals
|
|
179
|
+
form_class = Class.new(Blanks::Base) do
|
|
180
|
+
model_name_for :test
|
|
181
|
+
attribute :title, :string
|
|
182
|
+
attribute :content, :string
|
|
183
|
+
attribute :status, :string
|
|
184
|
+
inherit_validations_from model_class
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
validators = form_class.validators.map { |v| [v.attributes, v.kind] }
|
|
188
|
+
|
|
189
|
+
expect(validators).to include([[:title], :presence])
|
|
190
|
+
expect(validators).to include([[:status], :presence])
|
|
191
|
+
expect(validators).not_to include([[:content], :presence])
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "inherits validators with symbol conditionals" do
|
|
195
|
+
model_class = @model_with_conditionals
|
|
196
|
+
form_class = Class.new(Blanks::Base) do
|
|
197
|
+
model_name_for :test
|
|
198
|
+
attribute :status, :string
|
|
199
|
+
inherit_validations_from model_class, only: [:status]
|
|
200
|
+
|
|
201
|
+
def published?
|
|
202
|
+
status == "published"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
form = form_class.new(status: nil)
|
|
207
|
+
form.valid?
|
|
208
|
+
expect(form.errors[:status]).to be_empty
|
|
209
|
+
|
|
210
|
+
form_published = form_class.new(status: "published")
|
|
211
|
+
form_published.instance_variable_set(:@status, nil)
|
|
212
|
+
form_published.valid?
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
describe "integration with inherit_attributes_from" do
|
|
217
|
+
it "works with inherited attributes" do
|
|
218
|
+
model_class = @model_class
|
|
219
|
+
form_class = Class.new(Blanks::Base) do
|
|
220
|
+
model_name_for :test
|
|
221
|
+
inherit_attributes_from model_class, only: [:title, :content]
|
|
222
|
+
inherit_validations_from model_class, only: [:title]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
form = form_class.new
|
|
226
|
+
form.valid?
|
|
227
|
+
|
|
228
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
229
|
+
expect(form.errors[:content]).to be_empty
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
describe "multiple calls" do
|
|
234
|
+
it "accumulates validators from multiple sources" do
|
|
235
|
+
model_class = @model_class
|
|
236
|
+
other_model = Class.new do
|
|
237
|
+
include ActiveModel::Model
|
|
238
|
+
include ActiveModel::Attributes
|
|
239
|
+
include ActiveModel::Validations
|
|
240
|
+
|
|
241
|
+
attribute :author, :string
|
|
242
|
+
validates :author, presence: true
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
form_class = Class.new(Blanks::Base) do
|
|
246
|
+
model_name_for :test
|
|
247
|
+
attribute :title, :string
|
|
248
|
+
attribute :author, :string
|
|
249
|
+
inherit_validations_from model_class, only: [:title]
|
|
250
|
+
inherit_validations_from other_model, only: [:author]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
form = form_class.new
|
|
254
|
+
form.valid?
|
|
255
|
+
|
|
256
|
+
expect(form.errors[:title]).to include("can't be blank")
|
|
257
|
+
expect(form.errors[:author]).to include("can't be blank")
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Blanks::ModelNaming do
|
|
6
|
+
describe ".model_name" do
|
|
7
|
+
it "strips Form suffix from class name" do
|
|
8
|
+
form_class = Class.new(Blanks::Base)
|
|
9
|
+
stub_const("PostForm", form_class)
|
|
10
|
+
|
|
11
|
+
expect(PostForm.model_name.name).to eq("Post")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "generates correct param_key" do
|
|
15
|
+
form_class = Class.new(Blanks::Base)
|
|
16
|
+
stub_const("PostForm", form_class)
|
|
17
|
+
|
|
18
|
+
expect(PostForm.model_name.param_key).to eq("post")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "generates correct route_key" do
|
|
22
|
+
form_class = Class.new(Blanks::Base)
|
|
23
|
+
stub_const("PostForm", form_class)
|
|
24
|
+
|
|
25
|
+
expect(PostForm.model_name.route_key).to eq("posts")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "handles class names without Form suffix" do
|
|
29
|
+
form_class = Class.new(Blanks::Base)
|
|
30
|
+
stub_const("Article", form_class)
|
|
31
|
+
|
|
32
|
+
expect(Article.model_name.name).to eq("Article")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe ".model_name_for" do
|
|
37
|
+
it "overrides the model name" do
|
|
38
|
+
form_class = Class.new(Blanks::Base) do
|
|
39
|
+
model_name_for :article
|
|
40
|
+
end
|
|
41
|
+
stub_const("AdminPostForm", form_class)
|
|
42
|
+
|
|
43
|
+
expect(AdminPostForm.model_name.name).to eq("Article")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "generates correct param_key from override" do
|
|
47
|
+
form_class = Class.new(Blanks::Base) do
|
|
48
|
+
model_name_for :article
|
|
49
|
+
end
|
|
50
|
+
stub_const("AdminPostForm", form_class)
|
|
51
|
+
|
|
52
|
+
expect(AdminPostForm.model_name.param_key).to eq("article")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "accepts symbol" do
|
|
56
|
+
form_class = Class.new(Blanks::Base) do
|
|
57
|
+
model_name_for :blog_post
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
expect(form_class.model_name.name).to eq("BlogPost")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "accepts string" do
|
|
64
|
+
form_class = Class.new(Blanks::Base) do
|
|
65
|
+
model_name_for "blog_post"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
expect(form_class.model_name.name).to eq("BlogPost")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#model_name" do
|
|
73
|
+
it "delegates to class method" do
|
|
74
|
+
form_class = Class.new(Blanks::Base)
|
|
75
|
+
stub_const("PostForm", form_class)
|
|
76
|
+
|
|
77
|
+
form = PostForm.new
|
|
78
|
+
|
|
79
|
+
expect(form.model_name.name).to eq("Post")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|