sample_models 1.2.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|