sample_models 1.2.6 → 2.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.
- data/Gemfile +6 -6
- data/Gemfile.lock +22 -7
- data/README.markdown +21 -42
- data/Rakefile +10 -14
- data/VERSION +1 -1
- data/lib/sample_models/attribute_sequence.rb +182 -0
- data/lib/sample_models/creation.rb +136 -89
- data/lib/sample_models/initializer.rb +48 -0
- data/lib/sample_models/model.rb +52 -143
- data/lib/sample_models/sampler.rb +64 -88
- data/lib/sample_models.rb +15 -95
- data/sample_models.gemspec +14 -18
- data/test/setup/models.rb +205 -0
- data/test/setup/schema.rb +108 -0
- data/test/test_helper.rb +42 -0
- data/test/unit/belongs_to_test.rb +143 -0
- data/test/unit/configuration_test.rb +85 -0
- data/test/unit/has_many_through_test.rb +46 -0
- data/test/unit/named_sample_test.rb +15 -0
- data/test/unit/polymorphic_belongs_to_test.rb +29 -0
- data/test/unit/sample_test.rb +134 -0
- metadata +16 -20
- data/lib/sample_models/finder.rb +0 -86
- data/spec/sample_models_spec.rb +0 -11
- data/spec_or_test/database.yml +0 -6
- data/spec_or_test/setup.rb +0 -288
- data/spec_or_test/specs_or_test_cases.rb +0 -586
- data/test/test_sample_models.rb +0 -28
- data/vendor/ar_query/MIT-LICENSE +0 -20
- data/vendor/ar_query/README +0 -0
- data/vendor/ar_query/ar_query.gemspec +0 -16
- data/vendor/ar_query/init.rb +0 -1
- data/vendor/ar_query/install.rb +0 -1
- data/vendor/ar_query/lib/ar_query.rb +0 -146
- data/vendor/ar_query/spec/ar_query_spec.rb +0 -318
- data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
- data/vendor/ar_query/uninstall.rb +0 -1
@@ -1,126 +1,173 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
1
3
|
module SampleModels
|
2
4
|
class Creation
|
3
|
-
def initialize(sampler,
|
5
|
+
def initialize(sampler, *args)
|
4
6
|
@sampler = sampler
|
5
|
-
@
|
6
|
-
@attrs = Attributes.new(@sampler, attrs)
|
7
|
+
@specified_attrs = SpecifiedAttributes.new(sampler, *args).result
|
7
8
|
end
|
8
9
|
|
9
|
-
def
|
10
|
-
|
10
|
+
def belongs_to_assoc(column_name)
|
11
|
+
model.belongs_to_associations.detect { |assoc|
|
12
|
+
assoc.foreign_key == column_name
|
13
|
+
}
|
11
14
|
end
|
12
15
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def deferred_belongs_to_assocs
|
17
|
+
@deferred_belongs_to_assocs ||= begin
|
18
|
+
model.belongs_to_associations.select { |a|
|
19
|
+
@instance.send(a.foreign_key).nil? &&
|
20
|
+
!@specified_attrs.member?(a.foreign_key) &&
|
21
|
+
!@specified_attrs.member?(a.name) &&
|
22
|
+
!@sampler.defaults.member?(a.foreign_key) &&
|
23
|
+
!@sampler.defaults.member?(a.name)
|
24
|
+
}
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
28
|
+
def model
|
29
|
+
@sampler.model
|
30
|
+
end
|
31
|
+
|
23
32
|
def run
|
24
|
-
|
33
|
+
attrs = @specified_attrs.clone
|
34
|
+
@sampler.defaults.each do |attr, val|
|
35
|
+
attrs[attr] = val unless attrs.member?(attr)
|
36
|
+
end
|
37
|
+
columns_to_fill = model.columns.clone
|
38
|
+
model.validated_attr_accessors.each do |attr|
|
39
|
+
columns_to_fill << VirtualColumn.new(attr)
|
40
|
+
end
|
41
|
+
columns_to_fill.each do |column|
|
42
|
+
unless attrs.member?(column.name) ||
|
43
|
+
specified_association_value?(column.name) ||
|
44
|
+
((assoc = belongs_to_assoc(column.name)) && attrs.member?(assoc.name)) || timestamp?(column.name)
|
45
|
+
sequence = @sampler.first_pass_attribute_sequence(column)
|
46
|
+
attrs[column.name] = sequence.value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@instance = model.new(attrs)
|
25
50
|
save!
|
26
|
-
|
51
|
+
update_with_deferred_associations!
|
27
52
|
@instance
|
28
53
|
end
|
29
54
|
|
30
55
|
def save!
|
31
|
-
@sampler.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
model.belongs_to_associations.each do |assoc|
|
37
|
-
unless @instance.send(assoc.name) || @attrs.has_key?(assoc.name) ||
|
38
|
-
@attrs.has_key?(assoc.association_foreign_key)
|
39
|
-
if assoc.options[:polymorphic]
|
40
|
-
needs_another_save = true
|
41
|
-
@instance.send "#{assoc.name}=", polymorphic_assoc_value(assoc)
|
42
|
-
elsif @sampler.model_class != assoc.klass
|
43
|
-
needs_another_save = true
|
44
|
-
@instance.send "#{assoc.name}=", assoc.klass.sample
|
45
|
-
end
|
56
|
+
if @sampler.before_save
|
57
|
+
if @sampler.before_save.arity == 1
|
58
|
+
@sampler.before_save.call(@instance)
|
59
|
+
else
|
60
|
+
@sampler.before_save.call(@instance, @specified_attrs)
|
46
61
|
end
|
47
62
|
end
|
48
|
-
save!
|
63
|
+
@instance.save!
|
49
64
|
end
|
50
65
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
super(hash)
|
56
|
-
fill_based_on_validations
|
57
|
-
fill_based_on_configured_defaults
|
58
|
-
model.columns.each do |column|
|
59
|
-
unless has_key?(column.name)
|
60
|
-
fill_based_on_column_type column
|
61
|
-
end
|
66
|
+
def specified_association_value?(column_name)
|
67
|
+
@specified_attrs.any? { |attr, val|
|
68
|
+
if assoc = model.belongs_to_association(attr)
|
69
|
+
assoc.foreign_key == column_name
|
62
70
|
end
|
63
|
-
|
64
|
-
|
65
|
-
def model
|
66
|
-
SampleModels.models[@sampler.model_class]
|
67
|
-
end
|
71
|
+
}
|
72
|
+
end
|
68
73
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
74
|
+
def timestamp?(column_name)
|
75
|
+
%w(created_at updated_at created_on updated_on).include?(column_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_with_deferred_associations!
|
79
|
+
unless deferred_belongs_to_assocs.empty?
|
80
|
+
deferred_belongs_to_assocs.each do |a|
|
81
|
+
if a.polymorphic?
|
82
|
+
klass = @sampler.polymorphic_default_classes[a.name]
|
83
|
+
klass ||= SampleModels.samplers.values.map(&:model).detect { |m|
|
84
|
+
m != @sampler.model
|
85
|
+
}
|
86
|
+
@instance.send("#{a.name}=", klass.sample)
|
87
|
+
else
|
88
|
+
column = model.columns.detect { |c| c.name == a.foreign_key }
|
89
|
+
@instance.send(
|
90
|
+
"#{a.foreign_key}=",
|
91
|
+
@sampler.second_pass_attribute_sequence(column).next
|
92
|
+
)
|
83
93
|
end
|
84
|
-
when :datetime
|
85
|
-
self[column.name] = Time.now.utc
|
86
|
-
when :float
|
87
|
-
self[column.name] = 1.0
|
88
94
|
end
|
95
|
+
save!
|
89
96
|
end
|
97
|
+
end
|
90
98
|
|
91
|
-
|
92
|
-
|
93
|
-
|
99
|
+
class SpecifiedAttributes
|
100
|
+
attr_reader :result
|
101
|
+
|
102
|
+
def initialize(sampler, *args)
|
103
|
+
@sampler = sampler
|
104
|
+
@result = if args.first.is_a?(Symbol)
|
105
|
+
sample_name = args.shift
|
106
|
+
@sampler.named_samples[sample_name].clone
|
107
|
+
else
|
108
|
+
{}
|
109
|
+
end
|
110
|
+
@result.merge!(args.pop) if args.last.is_a?(Hash)
|
111
|
+
args.each do |associated_value|
|
112
|
+
assign_associated_record_from_args(associated_value)
|
113
|
+
end
|
114
|
+
model.belongs_to_associations.each do |assoc|
|
115
|
+
build_belongs_to_record_from_shortcut_args(assoc)
|
94
116
|
end
|
117
|
+
model.has_many_associations.each do |assoc|
|
118
|
+
build_has_many_record_from_shortcut_args(assoc)
|
119
|
+
end
|
120
|
+
@result = HashWithIndifferentAccess.new(@result)
|
95
121
|
end
|
96
|
-
|
97
|
-
def
|
98
|
-
|
99
|
-
|
100
|
-
|
122
|
+
|
123
|
+
def assign_associated_record_from_args(associated_value)
|
124
|
+
assocs = model.associations.select { |a|
|
125
|
+
begin
|
126
|
+
a.klass == associated_value.class
|
127
|
+
rescue NameError
|
128
|
+
false
|
129
|
+
end
|
101
130
|
}
|
102
|
-
|
131
|
+
if assocs.size == 1
|
132
|
+
@result[assocs.first.name] = associated_value
|
133
|
+
else
|
134
|
+
raise "Not sure what to do with associated value #{associated_value.inspect}"
|
103
135
|
end
|
104
136
|
end
|
105
|
-
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
else
|
113
|
-
a.primary_key_name
|
114
|
-
end
|
115
|
-
foreign_key == field.to_s
|
116
|
-
}
|
117
|
-
assoc_key = assoc.name
|
118
|
-
end
|
119
|
-
unless has_key?(field) || (assoc_key && has_key?(assoc_key))
|
120
|
-
self[field] = validation_collection.satisfying_value
|
137
|
+
|
138
|
+
def build_belongs_to_record_from_shortcut_args(assoc)
|
139
|
+
if value = @result[assoc.name]
|
140
|
+
if value.is_a?(Hash)
|
141
|
+
@result[assoc.name] = assoc.klass.sample(value)
|
142
|
+
elsif value.is_a?(Array)
|
143
|
+
@result[assoc.name] = assoc.klass.sample(*value)
|
121
144
|
end
|
122
145
|
end
|
123
146
|
end
|
147
|
+
|
148
|
+
def build_has_many_record_from_shortcut_args(assoc)
|
149
|
+
if values = @result[assoc.name]
|
150
|
+
@result[assoc.name] = values.map { |value|
|
151
|
+
value.is_a?(Hash) ? assoc.klass.sample(value) : value
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def model
|
157
|
+
@sampler.model
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class VirtualColumn
|
162
|
+
attr_reader :name
|
163
|
+
|
164
|
+
def initialize(name)
|
165
|
+
@name = name
|
166
|
+
end
|
167
|
+
|
168
|
+
def type
|
169
|
+
:string
|
170
|
+
end
|
124
171
|
end
|
125
172
|
end
|
126
173
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SampleModels
|
2
|
+
class Initializer
|
3
|
+
def run
|
4
|
+
ActiveRecord::Base.send(:include, SampleModels)
|
5
|
+
intercept_validation_definitions
|
6
|
+
end
|
7
|
+
|
8
|
+
def intercept_validation_definition(validation, recipient)
|
9
|
+
method_name = "#{validation}_with_sample_models".to_sym
|
10
|
+
recipient.send(:define_method, method_name) do |*args|
|
11
|
+
send "#{validation}_without_sample_models".to_sym, *args
|
12
|
+
SampleModels.models[self].record_validation(validation, *args)
|
13
|
+
end
|
14
|
+
recipient.alias_method_chain validation, :sample_models
|
15
|
+
end
|
16
|
+
|
17
|
+
def intercept_validation_definitions
|
18
|
+
validations_to_intercept = [
|
19
|
+
:validates_email_format_of, :validates_inclusion_of,
|
20
|
+
:validates_presence_of, :validates_uniqueness_of
|
21
|
+
]
|
22
|
+
optional_interceptions = [:validates_email_format_of]
|
23
|
+
validations_to_intercept.each do |validation|
|
24
|
+
recipient = validation_recipients.detect { |vr|
|
25
|
+
vr.method_defined?(validation)
|
26
|
+
}
|
27
|
+
if recipient
|
28
|
+
intercept_validation_definition(validation, recipient)
|
29
|
+
else
|
30
|
+
unless optional_interceptions.include?(validation)
|
31
|
+
raise "Can't find who defines the validation method #{validation}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validation_recipients
|
38
|
+
validation_recipients = [ActiveRecord::Validations::ClassMethods]
|
39
|
+
if Object.const_defined?('ActiveModel')
|
40
|
+
validation_recipients << ActiveModel::Validations::HelperMethods
|
41
|
+
end
|
42
|
+
if Object.const_defined?('ValidatesEmailFormatOf')
|
43
|
+
validation_recipients << ValidatesEmailFormatOf::Validations
|
44
|
+
end
|
45
|
+
validation_recipients
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/sample_models/model.rb
CHANGED
@@ -2,48 +2,31 @@ require 'delegate'
|
|
2
2
|
|
3
3
|
module SampleModels
|
4
4
|
class Model < Delegator
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :ar_class
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
10
|
-
h[field] = ValidationCollection.new(self, field)
|
11
|
-
}
|
7
|
+
def initialize(ar_class)
|
8
|
+
@ar_class = ar_class
|
9
|
+
@validations = Hash.new { |h,k| h[k] = [] }
|
12
10
|
end
|
13
11
|
|
14
12
|
def __getobj__
|
15
|
-
@
|
13
|
+
@ar_class
|
16
14
|
end
|
17
15
|
|
18
|
-
def associations
|
19
|
-
@
|
20
|
-
begin
|
21
|
-
a.klass == klass
|
22
|
-
rescue NameError
|
23
|
-
false
|
24
|
-
end
|
25
|
-
}
|
16
|
+
def associations
|
17
|
+
@ar_class.reflect_on_all_associations.map { |a| Association.new(a) }
|
26
18
|
end
|
27
19
|
|
28
|
-
def
|
29
|
-
|
30
|
-
assoc.macro == :belongs_to
|
31
|
-
}
|
20
|
+
def belongs_to_association(name)
|
21
|
+
belongs_to_associations.detect { |a| a.name.to_s == name.to_s }
|
32
22
|
end
|
33
23
|
|
34
|
-
def
|
35
|
-
|
36
|
-
@model_class.scoped(nil).respond_to?(:apply_finder_options)
|
37
|
-
@model_class.scoped(nil).apply_finder_options(*args).arel.to_sql
|
38
|
-
else
|
39
|
-
@model_class.send(:construct_finder_sql, *args)
|
40
|
-
end
|
24
|
+
def belongs_to_associations
|
25
|
+
associations.select { |a| a.belongs_to? }
|
41
26
|
end
|
42
27
|
|
43
28
|
def has_many_associations
|
44
|
-
|
45
|
-
assoc.macro == :has_many
|
46
|
-
}
|
29
|
+
associations.select { |a| a.has_many? }
|
47
30
|
end
|
48
31
|
|
49
32
|
def record_validation(*args)
|
@@ -51,145 +34,71 @@ module SampleModels
|
|
51
34
|
config = args.extract_options!
|
52
35
|
fields = args
|
53
36
|
fields.each do |field|
|
54
|
-
@
|
37
|
+
@validations[field.to_s] << Validation.new(type, config)
|
55
38
|
end
|
56
39
|
end
|
57
40
|
|
58
|
-
def
|
59
|
-
@
|
41
|
+
def unique?(field, value)
|
42
|
+
@ar_class.count(:conditions => {field => value}) == 0
|
60
43
|
end
|
61
44
|
|
62
|
-
def
|
63
|
-
|
45
|
+
def validated_attr_accessors
|
46
|
+
@validations.keys.select { |key|
|
47
|
+
columns.none? { |column| column.name.to_s == key.to_s }
|
48
|
+
}
|
64
49
|
end
|
65
50
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
stream_class_name = validation_method.to_s.camelize + 'ValueStream'
|
74
|
-
if ValidationCollection.const_defined?(stream_class_name)
|
75
|
-
stream_class = ValidationCollection.const_get(stream_class_name)
|
76
|
-
if stream_class == ValidatesUniquenessOfValueStream
|
77
|
-
@validates_uniqueness_config = config
|
78
|
-
else
|
79
|
-
input = @value_streams.last
|
80
|
-
@value_streams << stream_class.new(@model, @field, config, input)
|
81
|
-
end
|
82
|
-
end
|
51
|
+
def validations(name)
|
52
|
+
@validations[name.to_s]
|
53
|
+
end
|
54
|
+
|
55
|
+
class Association < Delegator
|
56
|
+
def initialize(assoc)
|
57
|
+
@assoc = assoc
|
83
58
|
end
|
84
59
|
|
85
|
-
def
|
86
|
-
@
|
60
|
+
def __getobj__
|
61
|
+
@assoc
|
87
62
|
end
|
88
63
|
|
89
|
-
def
|
90
|
-
@
|
64
|
+
def belongs_to?
|
65
|
+
@assoc.macro == :belongs_to
|
91
66
|
end
|
92
67
|
|
93
|
-
def
|
94
|
-
if @
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
)
|
99
|
-
@validates_uniqueness_config = nil
|
68
|
+
def foreign_key
|
69
|
+
if @assoc.respond_to?(:foreign_key)
|
70
|
+
@assoc.foreign_key
|
71
|
+
else
|
72
|
+
@assoc.primary_key_name
|
100
73
|
end
|
101
|
-
@value_streams.last.satisfying_value if @value_streams.last
|
102
74
|
end
|
103
75
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
def initialize(model, field, config, input)
|
108
|
-
@model, @field, @config, @input = model, field, config, input
|
109
|
-
@sequence_number = 0
|
110
|
-
end
|
111
|
-
|
112
|
-
def column
|
113
|
-
@model.columns.detect { |c| c.name == @field.to_s }
|
114
|
-
end
|
115
|
-
|
116
|
-
def increment
|
117
|
-
@sequence_number += 1
|
118
|
-
input.increment if input
|
119
|
-
end
|
76
|
+
def has_many?
|
77
|
+
@assoc.macro == :has_many
|
120
78
|
end
|
121
79
|
|
122
|
-
|
123
|
-
|
124
|
-
"john.doe#{@sequence_number}@example.com"
|
125
|
-
end
|
80
|
+
def polymorphic?
|
81
|
+
@assoc.options[:polymorphic]
|
126
82
|
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Validation
|
86
|
+
attr_reader :config, :type
|
127
87
|
|
128
|
-
|
129
|
-
|
130
|
-
@config[:in].first
|
131
|
-
end
|
88
|
+
def initialize(type, config = {})
|
89
|
+
@type, @config = type, config
|
132
90
|
end
|
133
91
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
a.association_foreign_key.to_sym == @field.to_sym
|
138
|
-
}
|
139
|
-
end
|
140
|
-
|
141
|
-
def increment
|
142
|
-
if association
|
143
|
-
association.klass.create_sample
|
144
|
-
else
|
145
|
-
super
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def satisfying_associated_value
|
150
|
-
value = association.klass.last || association.klass.sample
|
151
|
-
value = value.id if value
|
152
|
-
value
|
153
|
-
end
|
92
|
+
def email_format?
|
93
|
+
@type == :validates_email_format_of
|
94
|
+
end
|
154
95
|
|
155
|
-
|
156
|
-
|
157
|
-
if association
|
158
|
-
satisfying_associated_value
|
159
|
-
else
|
160
|
-
if prev_value.present?
|
161
|
-
prev_value
|
162
|
-
elsif column && column.type == :date
|
163
|
-
Date.today + @sequence_number
|
164
|
-
elsif column && column.type == :datetime
|
165
|
-
Time.utc(1970, 1, 1) + @sequence_number.days
|
166
|
-
elsif column && column.type == :integer
|
167
|
-
@sequence_number
|
168
|
-
else
|
169
|
-
"#{@field} #{@sequence_number}"
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
96
|
+
def inclusion?
|
97
|
+
@type == :validates_inclusion_of
|
173
98
|
end
|
174
99
|
|
175
|
-
|
176
|
-
|
177
|
-
value = input.satisfying_value if input
|
178
|
-
unless @config[:allow_nil] && value.nil?
|
179
|
-
unless @config[:allow_blank] && value.blank?
|
180
|
-
if !@model.unique?(@field, value)
|
181
|
-
my_input = input || ValidatesPresenceOfValueStream.new(
|
182
|
-
@model, @field, nil, @input
|
183
|
-
)
|
184
|
-
until @model.unique?(@field, value)
|
185
|
-
my_input.increment
|
186
|
-
value = my_input.satisfying_value
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
value
|
192
|
-
end
|
100
|
+
def presence?
|
101
|
+
@type == :validates_presence_of
|
193
102
|
end
|
194
103
|
end
|
195
104
|
end
|