seedie 0.3.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +91 -9
- data/CHANGELOG.md +101 -0
- data/Gemfile +4 -1
- data/README.md +18 -9
- data/Rakefile +1 -1
- data/lib/generators/seedie/install_generator.rb +28 -30
- data/lib/generators/seedie/templates/seedie_initializer.rb +15 -1
- data/lib/seedie/associations/base_association.rb +35 -31
- data/lib/seedie/associations/belongs_to.rb +12 -13
- data/lib/seedie/associations/has_and_belongs_to_many.rb +26 -0
- data/lib/seedie/associations/has_many.rb +6 -4
- data/lib/seedie/associations/has_one.rb +13 -13
- data/lib/seedie/configuration.rb +21 -1
- data/lib/seedie/field_values/custom_value.rb +14 -83
- data/lib/seedie/field_values/fake_value.rb +85 -17
- data/lib/seedie/field_values/faker_builder.rb +36 -36
- data/lib/seedie/field_values/value_template_validator.rb +91 -0
- data/lib/seedie/field_values_set.rb +21 -4
- data/lib/seedie/model/creator.rb +7 -5
- data/lib/seedie/model/id_generator.rb +10 -8
- data/lib/seedie/model/model_sorter.rb +14 -18
- data/lib/seedie/model_fields.rb +5 -3
- data/lib/seedie/model_seeder.rb +11 -22
- data/lib/seedie/polymorphic_association_helper.rb +20 -16
- data/lib/seedie/railtie.rb +3 -2
- data/lib/seedie/reporters/base_reporter.rb +75 -68
- data/lib/seedie/reporters/console_reporter.rb +16 -12
- data/lib/seedie/reporters/reportable.rb +14 -10
- data/lib/seedie/seeder.rb +5 -3
- data/lib/seedie/version.rb +1 -1
- data/lib/seedie.rb +11 -28
- data/lib/tasks/seedie.rake +4 -2
- metadata +28 -12
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seedie
|
4
|
+
module Associations
|
5
|
+
class HasAndBelongsToMany < BaseAssociation
|
6
|
+
def generate_associations
|
7
|
+
return if association_config["has_and_belongs_to_many"].nil?
|
8
|
+
|
9
|
+
report(:has_and_belongs_to_many_start)
|
10
|
+
association_config["has_and_belongs_to_many"].each do |association_name, association_config|
|
11
|
+
association_class = association_name.to_s.classify.constantize
|
12
|
+
count = get_association_count(association_config)
|
13
|
+
config = only_count_given?(association_config) ? {} : association_config
|
14
|
+
|
15
|
+
report(:associated_records, name: association_name, count: count, parent_name: model.to_s)
|
16
|
+
count.times do |index|
|
17
|
+
field_values_set = FieldValuesSet.new(association_class, config, index).generate_field_values_with_associations
|
18
|
+
record_creator = Model::Creator.new(record.send(association_name), reporters)
|
19
|
+
|
20
|
+
record_creator.create!(field_values_set)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,18 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
module Associations
|
3
5
|
class HasMany < BaseAssociation
|
4
6
|
def generate_associations
|
5
7
|
return if association_config["has_many"].nil?
|
6
|
-
|
8
|
+
|
7
9
|
report(:has_many_start)
|
8
10
|
association_config["has_many"].each do |association_name, association_config|
|
9
11
|
association_class = association_name.to_s.classify.constantize
|
10
12
|
count = get_association_count(association_config)
|
11
13
|
config = only_count_given?(association_config) ? {} : association_config
|
12
|
-
|
14
|
+
|
13
15
|
report(:associated_records, name: association_name, count: count, parent_name: model.to_s)
|
14
16
|
count.times do |index|
|
15
|
-
field_values_set = FieldValuesSet.new(association_class, config, index).
|
17
|
+
field_values_set = FieldValuesSet.new(association_class, config, index).generate_field_values_with_associations
|
16
18
|
record_creator = Model::Creator.new(record.send(association_name), reporters)
|
17
19
|
|
18
20
|
record_creator.create!(field_values_set)
|
@@ -21,4 +23,4 @@ module Seedie
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
24
|
-
end
|
26
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
module Associations
|
3
5
|
class HasOne < BaseAssociation
|
@@ -5,25 +7,23 @@ module Seedie
|
|
5
7
|
return if association_config["has_one"].nil?
|
6
8
|
|
7
9
|
report(:has_one_start)
|
8
|
-
|
10
|
+
|
9
11
|
association_config["has_one"].each do |association_name, association_config|
|
10
12
|
reflection = model.reflect_on_association(association_name)
|
11
13
|
association_class = reflection.klass
|
12
14
|
count = get_association_count(association_config)
|
13
|
-
|
15
|
+
|
14
16
|
report(:associated_records, count: count, name: association_name, parent_name: model.to_s)
|
15
|
-
if count > 1
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
record_creator.create!(field_values_set.merge!(parent_field_set))
|
24
|
-
end
|
17
|
+
raise InvalidAssociationConfigError, "has_one association cannot be more than 1" if count > 1
|
18
|
+
|
19
|
+
config = only_count_given?(association_config) ? {} : association_config
|
20
|
+
field_values_set = FieldValuesSet.new(association_class, config, INDEX).generate_field_values
|
21
|
+
parent_field_set = generate_associated_field(record.id, reflection.foreign_key)
|
22
|
+
|
23
|
+
record_creator = Model::Creator.new(association_class, reporters)
|
24
|
+
record_creator.create!(field_values_set.merge!(parent_field_set))
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
data/lib/seedie/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
class Configuration
|
3
5
|
attr_accessor :default_count, :custom_attributes
|
@@ -6,5 +8,23 @@ module Seedie
|
|
6
8
|
@default_count = nil
|
7
9
|
@custom_attributes = {}
|
8
10
|
end
|
11
|
+
|
12
|
+
# Prepares the custom_attributes hash for the specified models.
|
13
|
+
#
|
14
|
+
# This method ensures that the necessary keys exist in the custom_attributes hash.
|
15
|
+
# This prevents NoMethodError when setting model-specific custom attributes.
|
16
|
+
#
|
17
|
+
# Example usage:
|
18
|
+
# config.prepare_custom_attributes_for :user, :account
|
19
|
+
#
|
20
|
+
# Then this will work:
|
21
|
+
# config.custom_attributes[:user][:name] = "Name"
|
22
|
+
# config.custom_attributes[:account][:email] = "email@example.com"
|
23
|
+
#
|
24
|
+
def prepare_custom_attributes_for(*models)
|
25
|
+
models.inject(@custom_attributes) do |hash, key|
|
26
|
+
hash[key] ||= {}
|
27
|
+
end
|
28
|
+
end
|
9
29
|
end
|
10
|
-
end
|
30
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
module FieldValues
|
3
5
|
class CustomValue
|
4
|
-
VALID_KEYS = ["values", "value", "options"].freeze
|
5
|
-
PICK_STRATEGIES = ["random", "sequential"].freeze
|
6
|
-
|
7
6
|
attr_reader :name, :parsed_value
|
8
7
|
|
9
8
|
def initialize(name, value_template, index)
|
@@ -12,7 +11,7 @@ module Seedie
|
|
12
11
|
@index = index
|
13
12
|
@parsed_value = ""
|
14
13
|
|
15
|
-
|
14
|
+
ValueTemplateValidator.new(@value_template, @index, @name).validate
|
16
15
|
end
|
17
16
|
|
18
17
|
def generate_custom_field_value
|
@@ -27,89 +26,21 @@ module Seedie
|
|
27
26
|
|
28
27
|
private
|
29
28
|
|
30
|
-
def validate_value_template
|
31
|
-
return unless @value_template.is_a?(Hash)
|
32
|
-
|
33
|
-
validate_keys
|
34
|
-
validate_values if @value_template.key?("values")
|
35
|
-
validate_options if @value_template.key?("options")
|
36
|
-
end
|
37
|
-
|
38
|
-
def validate_values
|
39
|
-
values = @value_template["values"]
|
40
|
-
options = @value_template["options"]
|
41
|
-
|
42
|
-
if values.is_a?(Array) || values.is_a?(Hash)
|
43
|
-
validate_sequential_values_length
|
44
|
-
else
|
45
|
-
raise InvalidCustomFieldValuesError, "The values key for #{@name} must be an array or a hash with start and end keys."
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def validate_options
|
50
|
-
options = @value_template["options"]
|
51
|
-
pick_strategy = options["pick_strategy"]
|
52
|
-
|
53
|
-
if pick_strategy.present? && !PICK_STRATEGIES.include?(pick_strategy)
|
54
|
-
raise InvalidCustomFieldOptionsError,
|
55
|
-
"The pick_strategy for #{@name} must be either 'sequential' or 'random'."
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
## If pick strategy is sequential, we need to ensure there is a value for each index
|
60
|
-
# If there isn't sufficient values, we raise an error
|
61
|
-
def validate_sequential_values_length
|
62
|
-
return unless @value_template.key?("options")
|
63
|
-
return unless @value_template["options"]["pick_strategy"] == "sequential"
|
64
|
-
|
65
|
-
values = @value_template["values"]
|
66
|
-
|
67
|
-
if values.is_a?(Hash) && values.keys.sort == ["end", "start"]
|
68
|
-
# Assuming the values are an inclusive range
|
69
|
-
values_length = values["end"] - values["start"] + 1
|
70
|
-
else
|
71
|
-
values_length = values.length
|
72
|
-
end
|
73
|
-
|
74
|
-
if values_length < @index + 1
|
75
|
-
raise CustomFieldNotEnoughValuesError,
|
76
|
-
"There are not enough values for #{@name}. Please add more values."
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def validate_keys
|
81
|
-
invalid_keys = @value_template.keys - VALID_KEYS
|
82
|
-
|
83
|
-
if invalid_keys.present?
|
84
|
-
raise InvalidCustomFieldKeysError,
|
85
|
-
"Invalid keys for #{@name}: #{invalid_keys.join(", ")}. Only #{VALID_KEYS} are allowed."
|
86
|
-
end
|
87
|
-
|
88
|
-
if @value_template.key?("values")
|
89
|
-
if @value_template.key?("value")
|
90
|
-
raise InvalidCustomFieldKeysError,
|
91
|
-
"Invalid keys for #{@name}: values and value cannot be used together."
|
92
|
-
end
|
93
|
-
|
94
|
-
if @value_template["values"].is_a?(Hash)
|
95
|
-
if !@value_template["values"].key?("start") || !@value_template["values"].key?("end")
|
96
|
-
raise InvalidCustomFieldValuesError,
|
97
|
-
"The values key for #{@name} must be an array or a hash with start and end keys."
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
29
|
def generate_custom_value_from_string
|
104
30
|
@parsed_value = @value_template.gsub("{{index}}", @index.to_s)
|
105
31
|
|
106
32
|
@parsed_value.gsub!(/\{\{(.+?)\}\}/) do
|
107
|
-
method_string =
|
33
|
+
method_string = ::Regexp.last_match(1)
|
108
34
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
35
|
+
raise InvalidFakerMethodError, "Invalid method: #{method_string}" unless method_string.start_with?("Faker::")
|
36
|
+
|
37
|
+
method_chain = method_string.split(".")
|
38
|
+
# Faker::Name will be shifted off the array
|
39
|
+
faker_class = method_chain.shift.constantize
|
40
|
+
|
41
|
+
# For Faker::Internet.unique.email, there will be two methods in the array
|
42
|
+
method_chain.reduce(faker_class) do |current_class_or_value, method|
|
43
|
+
current_class_or_value.public_send(method)
|
113
44
|
end
|
114
45
|
end
|
115
46
|
end
|
@@ -123,7 +54,7 @@ module Seedie
|
|
123
54
|
generate_custom_values_from_range(@value_template["values"]["start"], @value_template["values"]["end"])
|
124
55
|
end
|
125
56
|
options = @value_template["options"]
|
126
|
-
|
57
|
+
|
127
58
|
if options.present? && options["pick_strategy"] == "sequential"
|
128
59
|
@parsed_value = values[@index]
|
129
60
|
else
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
module FieldValues
|
3
5
|
class FakeValue
|
@@ -9,41 +11,107 @@ module Seedie
|
|
9
11
|
def generate_fake_value
|
10
12
|
case @column.type
|
11
13
|
when :string, :text, :citext
|
12
|
-
|
14
|
+
generate_string
|
13
15
|
when :uuid
|
14
|
-
|
16
|
+
generate_uuid
|
15
17
|
when :integer, :bigint, :smallint
|
16
|
-
|
18
|
+
generate_integer
|
17
19
|
when :decimal, :float, :real
|
18
|
-
|
20
|
+
generate_decimal
|
19
21
|
when :datetime, :timestamp, :timestamptz
|
20
|
-
|
22
|
+
generate_datetime
|
21
23
|
when :date
|
22
|
-
|
24
|
+
generate_date
|
23
25
|
when :time, :timetz
|
24
|
-
|
26
|
+
generate_time
|
25
27
|
when :boolean
|
26
|
-
|
28
|
+
generate_boolean
|
27
29
|
when :json, :jsonb
|
28
|
-
|
30
|
+
generate_json
|
29
31
|
when :inet
|
30
|
-
|
32
|
+
generate_inet
|
31
33
|
when :cidr, :macaddr
|
32
|
-
|
34
|
+
generate_macaddr
|
33
35
|
when :bytea
|
34
|
-
|
36
|
+
generate_bytea
|
35
37
|
when :bit, :bit_varying
|
36
|
-
|
38
|
+
generate_bit
|
37
39
|
when :money
|
38
|
-
|
40
|
+
generate_money
|
39
41
|
when :hstore
|
40
|
-
|
42
|
+
generate_hstore
|
41
43
|
when :year
|
42
|
-
|
44
|
+
generate_year
|
43
45
|
else
|
44
46
|
raise UnknownColumnTypeError, "Unknown column type: #{@column.type}"
|
45
47
|
end
|
46
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def generate_string
|
53
|
+
Faker::Lorem.word
|
54
|
+
end
|
55
|
+
|
56
|
+
def generate_uuid
|
57
|
+
SecureRandom.uuid
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate_integer
|
61
|
+
Faker::Number.number(digits: 5)
|
62
|
+
end
|
63
|
+
|
64
|
+
def generate_decimal
|
65
|
+
Faker::Number.decimal(l_digits: 2, r_digits: 2)
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_datetime
|
69
|
+
Faker::Time.between(from: DateTime.now - 1, to: DateTime.now)
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_date
|
73
|
+
Faker::Date.between(from: Date.today - 2, to: Date.today)
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_time
|
77
|
+
Faker::Time.forward(days: 23, period: :morning)
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_boolean
|
81
|
+
Faker::Boolean.boolean
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_json
|
85
|
+
{ "value" => { "key1" => Faker::Lorem.word, "key2" => Faker::Number.number(digits: 2) } }
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_inet
|
89
|
+
Faker::Internet.ip_v4_address
|
90
|
+
end
|
91
|
+
|
92
|
+
def generate_macaddr
|
93
|
+
Faker::Internet.mac_address
|
94
|
+
end
|
95
|
+
|
96
|
+
def generate_bytea
|
97
|
+
Faker::Internet.password
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_bit
|
101
|
+
%w[0 1].sample
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_money
|
105
|
+
Faker::Commerce.price.to_s
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_hstore
|
109
|
+
{ "value" => { "key1" => Faker::Lorem.word, "key2" => Faker::Number.number(digits: 2) } }
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_year
|
113
|
+
rand(1901..2155)
|
114
|
+
end
|
47
115
|
end
|
48
116
|
end
|
49
|
-
end
|
117
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
module FieldValues
|
3
5
|
class FakerBuilder
|
@@ -14,14 +16,15 @@ module Seedie
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def build_faker_constant
|
17
|
-
|
19
|
+
custom_attribute = fetch_custom_attribute
|
20
|
+
return custom_attribute if fetch_custom_attribute
|
18
21
|
|
19
22
|
@unique_prefix = "unique." if has_validation?(:uniqueness)
|
20
|
-
|
23
|
+
|
21
24
|
add_faker_class_and_method(@column.type)
|
22
|
-
|
25
|
+
|
23
26
|
if has_validation?(:inclusion)
|
24
|
-
handle_inclusion_validation
|
27
|
+
handle_inclusion_validation
|
25
28
|
else
|
26
29
|
@options += handle_numericality_validation if has_validation?(:numericality)
|
27
30
|
@options += handle_length_validation if has_validation?(:length)
|
@@ -37,61 +40,57 @@ module Seedie
|
|
37
40
|
|
38
41
|
private
|
39
42
|
|
43
|
+
def fetch_custom_attribute
|
44
|
+
if @seedie_config_custom_attributes[@name.to_sym].is_a?(Hash)
|
45
|
+
return @seedie_config_custom_attributes.dig(@name.to_sym, @column.name.to_sym)
|
46
|
+
end
|
47
|
+
|
48
|
+
@seedie_config_custom_attributes[@name.to_sym]
|
49
|
+
end
|
50
|
+
|
40
51
|
def add_faker_class_and_method(type)
|
41
52
|
case type
|
42
53
|
when :string, :text, :citext
|
43
|
-
|
44
|
-
@method_prefix = "word"
|
54
|
+
set_faker("Lorem.", "word")
|
45
55
|
when :uuid
|
46
|
-
|
47
|
-
@method_prefix = "uuid"
|
56
|
+
set_faker("Internet.", "uuid")
|
48
57
|
when :integer, :bigint, :smallint
|
49
|
-
|
50
|
-
@method_prefix = "number"
|
51
|
-
@options = "(digits: 5)"
|
58
|
+
set_faker("Number.", "number", "(digits: 5)")
|
52
59
|
when :decimal, :float, :real
|
53
|
-
|
54
|
-
@method_prefix = "decimal"
|
55
|
-
@options = "(l_digits: 2, r_digits: 2)"
|
60
|
+
set_faker("Number.", "decimal", "(l_digits: 2, r_digits: 2)")
|
56
61
|
when :datetime, :timestamp, :timestamptz, :time, :timetz
|
57
|
-
|
58
|
-
@method_prefix = "between"
|
59
|
-
@options = "(from: DateTime.now - 1, to: DateTime.now)"
|
62
|
+
set_faker("Time.", "between", "(from: DateTime.now - 1, to: DateTime.now)")
|
60
63
|
when :date
|
61
|
-
|
62
|
-
@method_prefix = "between"
|
63
|
-
@options = "(from: Date.today - 1, to: Date.today)"
|
64
|
+
set_faker("Date.", "between", "(from: Date.today - 1, to: Date.today)")
|
64
65
|
when :boolean
|
65
|
-
|
66
|
-
@method_prefix = "boolean"
|
66
|
+
set_faker("Boolean.", "boolean")
|
67
67
|
when :json, :jsonb
|
68
68
|
@faker_expression = { "value" => "Json.shallow_json(width: 3, options: { key: 'Name.first_name', value: 'Number.number(digits: 2)' })" }
|
69
69
|
when :inet
|
70
|
-
|
71
|
-
@method_prefix = "ip_v4_address"
|
70
|
+
set_faker("Internet.", "ip_v4_address")
|
72
71
|
when :cidr, :macaddr
|
73
|
-
|
74
|
-
@method_prefix = "mac_address"
|
72
|
+
set_faker("Internet.", "mac_address")
|
75
73
|
when :bytea
|
76
|
-
|
77
|
-
@method_prefix = "password"
|
74
|
+
set_faker("Internet.", "password")
|
78
75
|
when :bit, :bit_varying
|
79
|
-
|
80
|
-
@method_prefix = "password"
|
76
|
+
set_faker("Internet.", "password")
|
81
77
|
when :money
|
82
|
-
|
83
|
-
@method_prefix = "price.to_s"
|
78
|
+
set_faker("Commerce.", "price.to_s")
|
84
79
|
when :hstore
|
85
80
|
@faker_expression = { "value" => "Json.shallow_json(width: 3, options: { key: 'Name.first_name', value: 'Number.number(digits: 2)' })" }
|
86
81
|
when :year
|
87
|
-
|
88
|
-
@method_prefix = "number"
|
89
|
-
@options = "(digits: 4)"
|
82
|
+
set_faker("Number.", "number", "(digits: 4)")
|
90
83
|
else
|
91
84
|
raise UnknownColumnTypeError, "Unknown column type: #{type}"
|
92
85
|
end
|
93
86
|
end
|
94
87
|
|
88
|
+
def set_faker(class_prefix, method_prefix, options = "")
|
89
|
+
@class_prefix = class_prefix
|
90
|
+
@method_prefix = method_prefix
|
91
|
+
@options = options
|
92
|
+
end
|
93
|
+
|
95
94
|
def has_validation?(kind)
|
96
95
|
@validations.any? { |validation| validation.kind == kind }
|
97
96
|
end
|
@@ -132,7 +131,8 @@ module Seedie
|
|
132
131
|
@method_prefix = ""
|
133
132
|
@options = ""
|
134
133
|
if options[:in].is_a?(Range)
|
135
|
-
@faker_expression = { "values" => { "start" => options[:in].first, "end" => options[:in].last },
|
134
|
+
@faker_expression = { "values" => { "start" => options[:in].first, "end" => options[:in].last },
|
135
|
+
"options" => { "pick_strategy" => "random" } }
|
136
136
|
else
|
137
137
|
@faker_expression = { "values" => options[:in], "options" => { "pick_strategy" => "random" } }
|
138
138
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seedie
|
4
|
+
module FieldValues
|
5
|
+
class ValueTemplateValidator
|
6
|
+
VALID_KEYS = %w[values value options].freeze
|
7
|
+
PICK_STRATEGIES = %w[random sequential].freeze
|
8
|
+
|
9
|
+
def initialize(value_template, index, name)
|
10
|
+
@value_template = value_template
|
11
|
+
@index = index
|
12
|
+
@name = name
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
return unless @value_template.is_a?(Hash)
|
17
|
+
|
18
|
+
validate_keys
|
19
|
+
validate_values if @value_template.key?("values")
|
20
|
+
validate_options if @value_template.key?("options")
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def validate_keys
|
26
|
+
invalid_keys = @value_template.keys - VALID_KEYS
|
27
|
+
|
28
|
+
if invalid_keys.present?
|
29
|
+
raise InvalidCustomFieldKeysError,
|
30
|
+
"Invalid keys for #{@name}: #{invalid_keys.join(', ')}. Only #{VALID_KEYS} are allowed."
|
31
|
+
end
|
32
|
+
|
33
|
+
return unless @value_template.key?("values")
|
34
|
+
|
35
|
+
if @value_template.key?("value")
|
36
|
+
raise InvalidCustomFieldKeysError,
|
37
|
+
"Invalid keys for #{@name}: values and value cannot be used together."
|
38
|
+
end
|
39
|
+
|
40
|
+
return unless @value_template["values"].is_a?(Hash)
|
41
|
+
|
42
|
+
return unless !@value_template["values"].key?("start") || !@value_template["values"].key?("end")
|
43
|
+
|
44
|
+
raise InvalidCustomFieldValuesError,
|
45
|
+
"The values key for #{@name} must be an array or a hash with start and end keys."
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_values
|
49
|
+
values = @value_template["values"]
|
50
|
+
|
51
|
+
unless values.is_a?(Array) || values.is_a?(Hash)
|
52
|
+
raise InvalidCustomFieldValuesError,
|
53
|
+
"The values key for #{@name} must be an array or a hash with start and end keys."
|
54
|
+
end
|
55
|
+
|
56
|
+
validate_sequential_values_length
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_options
|
60
|
+
options = @value_template["options"]
|
61
|
+
pick_strategy = options["pick_strategy"]
|
62
|
+
|
63
|
+
return unless pick_strategy.present? && !PICK_STRATEGIES.include?(pick_strategy)
|
64
|
+
|
65
|
+
raise InvalidCustomFieldOptionsError,
|
66
|
+
"The pick_strategy for #{@name} must be either 'sequential' or 'random'."
|
67
|
+
end
|
68
|
+
|
69
|
+
## If pick strategy is sequential, we need to ensure there is a value for each index
|
70
|
+
# If there isn't sufficient values, we raise an error
|
71
|
+
def validate_sequential_values_length
|
72
|
+
return unless @value_template.key?("options")
|
73
|
+
return unless @value_template["options"]["pick_strategy"] == "sequential"
|
74
|
+
|
75
|
+
values = @value_template["values"]
|
76
|
+
|
77
|
+
values_length = if values.is_a?(Hash) && values.keys.sort == %w[end start]
|
78
|
+
# Assuming the values are an inclusive range
|
79
|
+
values["end"] - values["start"] + 1
|
80
|
+
else
|
81
|
+
values.length
|
82
|
+
end
|
83
|
+
|
84
|
+
return unless values_length < @index + 1
|
85
|
+
|
86
|
+
raise CustomFieldNotEnoughValuesError,
|
87
|
+
"There are not enough values for #{@name}. Please add more values."
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seedie
|
2
4
|
class FieldValuesSet
|
3
|
-
attr_reader :attributes_config, :index
|
5
|
+
attr_reader :model, :model_config, :attributes_config, :index
|
4
6
|
|
5
7
|
def initialize(model, model_config, index)
|
6
8
|
@model = model
|
@@ -18,6 +20,12 @@ module Seedie
|
|
18
20
|
@field_values
|
19
21
|
end
|
20
22
|
|
23
|
+
def generate_field_values_with_associations
|
24
|
+
associated_field_values_set = generate_belongs_to_associations
|
25
|
+
model_field_values_set = generate_field_values
|
26
|
+
model_field_values_set.merge!(associated_field_values_set)
|
27
|
+
end
|
28
|
+
|
21
29
|
def generate_field_value(name, column)
|
22
30
|
return generate_custom_field_value(name) if @attributes_config&.key?(name)
|
23
31
|
|
@@ -26,18 +34,27 @@ module Seedie
|
|
26
34
|
|
27
35
|
private
|
28
36
|
|
37
|
+
def generate_belongs_to_associations
|
38
|
+
associations_config = model_config["associations"]
|
39
|
+
return {} unless associations_config.present?
|
40
|
+
|
41
|
+
belongs_to_associations = Associations::BelongsTo.new(model, associations_config)
|
42
|
+
belongs_to_associations.generate_associations
|
43
|
+
belongs_to_associations.associated_field_set
|
44
|
+
end
|
45
|
+
|
29
46
|
def populate_values_for_model_fields
|
30
47
|
@field_values = @model.columns_hash.map do |name, column|
|
31
48
|
next if @model_fields.disabled_fields.include?(name)
|
32
49
|
next if @model_fields.foreign_fields.include?(name)
|
33
|
-
|
50
|
+
|
34
51
|
[name, generate_field_value(name, column)]
|
35
52
|
end.compact.to_h
|
36
53
|
end
|
37
54
|
|
38
55
|
def populate_values_for_virtual_fields
|
39
56
|
virtual_fields = @attributes_config.keys - @model.columns_hash.keys
|
40
|
-
|
57
|
+
|
41
58
|
virtual_fields.each do |name|
|
42
59
|
@field_values[name] = generate_custom_field_value(name) if @attributes_config[name]
|
43
60
|
end
|
@@ -47,4 +64,4 @@ module Seedie
|
|
47
64
|
FieldValues::CustomValue.new(name, @attributes_config[name], @index).generate_custom_field_value
|
48
65
|
end
|
49
66
|
end
|
50
|
-
end
|
67
|
+
end
|