seedie 0.3.0 → 0.4.1
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 +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
|