sample_models 0.9.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/.gitignore +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +345 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/init.rb +2 -0
- data/install.rb +1 -0
- data/lib/sample_models/creation.rb +116 -0
- data/lib/sample_models/finder.rb +81 -0
- data/lib/sample_models/model.rb +136 -0
- data/lib/sample_models/sampler.rb +110 -0
- data/lib/sample_models.rb +117 -0
- data/sample_models.gemspec +89 -0
- data/spec/sample_models_spec.rb +11 -0
- data/spec_or_test/database.yml +7 -0
- data/spec_or_test/setup.rb +249 -0
- data/spec_or_test/specs_or_test_cases.rb +569 -0
- data/spec_or_test/vendor/validates_email_format_of/CHANGELOG +11 -0
- data/spec_or_test/vendor/validates_email_format_of/MIT-LICENSE +20 -0
- data/spec_or_test/vendor/validates_email_format_of/README +28 -0
- data/spec_or_test/vendor/validates_email_format_of/TODO +1 -0
- data/spec_or_test/vendor/validates_email_format_of/init.rb +1 -0
- data/spec_or_test/vendor/validates_email_format_of/lib/validates_email_format_of.rb +41 -0
- data/spec_or_test/vendor/validates_email_format_of/rakefile +28 -0
- data/spec_or_test/vendor/validates_email_format_of/test/database.yml +3 -0
- data/spec_or_test/vendor/validates_email_format_of/test/fixtures/people.yml +3 -0
- data/spec_or_test/vendor/validates_email_format_of/test/fixtures/person.rb +3 -0
- data/spec_or_test/vendor/validates_email_format_of/test/schema.rb +5 -0
- data/spec_or_test/vendor/validates_email_format_of/test/test_helper.rb +35 -0
- data/spec_or_test/vendor/validates_email_format_of/test/validates_email_format_of_test.rb +82 -0
- data/tasks/sample_models_tasks.rake +4 -0
- data/test/test_sample_models.rb +28 -0
- data/uninstall.rb +1 -0
- data/vendor/ar_query/MIT-LICENSE +20 -0
- data/vendor/ar_query/README +0 -0
- data/vendor/ar_query/ar_query.gemspec +16 -0
- data/vendor/ar_query/init.rb +1 -0
- data/vendor/ar_query/install.rb +1 -0
- data/vendor/ar_query/lib/ar_query.rb +146 -0
- data/vendor/ar_query/spec/ar_query_spec.rb +318 -0
- data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
- data/vendor/ar_query/uninstall.rb +1 -0
- metadata +117 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module SampleModels
|
4
|
+
class Model < Delegator
|
5
|
+
attr_reader :validation_collections
|
6
|
+
|
7
|
+
def initialize(model_class)
|
8
|
+
@model_class = model_class
|
9
|
+
@validation_collections = Hash.new { |h, field|
|
10
|
+
h[field] = ValidationCollection.new(self, field)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def __getobj__
|
15
|
+
@model_class
|
16
|
+
end
|
17
|
+
|
18
|
+
def associations(klass)
|
19
|
+
@model_class.reflect_on_all_associations.select { |a|
|
20
|
+
begin
|
21
|
+
a.klass == klass
|
22
|
+
rescue NameError
|
23
|
+
false
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def belongs_to_associations
|
29
|
+
@model_class.reflect_on_all_associations.select { |assoc|
|
30
|
+
assoc.macro == :belongs_to
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def construct_finder_sql(*args)
|
35
|
+
@model_class.send(:construct_finder_sql, *args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_many_associations
|
39
|
+
@model_class.reflect_on_all_associations.select { |assoc|
|
40
|
+
assoc.macro == :has_many
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def record_validation(*args)
|
45
|
+
type = args.shift
|
46
|
+
config = args.extract_options!
|
47
|
+
fields = args
|
48
|
+
fields.each do |field|
|
49
|
+
@validation_collections[field].add(type, config)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validates_presence_of?(attr)
|
54
|
+
@validation_collections[attr].includes_presence?
|
55
|
+
end
|
56
|
+
|
57
|
+
class ValidationCollection
|
58
|
+
def initialize(model, field)
|
59
|
+
@model, @field = model, field
|
60
|
+
@sequence_number = 0
|
61
|
+
@validations = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def add(type, config)
|
65
|
+
@validations[type] = config
|
66
|
+
end
|
67
|
+
|
68
|
+
def association
|
69
|
+
@model.belongs_to_associations.detect { |a|
|
70
|
+
a.association_foreign_key.to_sym == @field.to_sym
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def column
|
75
|
+
@model.columns.detect { |c| c.name == @field.to_s }
|
76
|
+
end
|
77
|
+
|
78
|
+
def includes_presence?
|
79
|
+
@validations.has_key?(:validates_presence_of)
|
80
|
+
end
|
81
|
+
|
82
|
+
def includes_uniqueness?
|
83
|
+
@validations.has_key?(:validates_uniqueness_of)
|
84
|
+
end
|
85
|
+
|
86
|
+
def satisfying_present_associated_value
|
87
|
+
value = if includes_uniqueness?
|
88
|
+
association.klass.create_sample
|
89
|
+
else
|
90
|
+
association.klass.first || association.klass.sample
|
91
|
+
end
|
92
|
+
value = value.id if value
|
93
|
+
value
|
94
|
+
end
|
95
|
+
|
96
|
+
def satisfying_present_value(prev_value)
|
97
|
+
if association
|
98
|
+
satisfying_present_associated_value
|
99
|
+
else
|
100
|
+
if prev_value.present?
|
101
|
+
prev_value
|
102
|
+
elsif column && column.type == :date
|
103
|
+
Date.today
|
104
|
+
else
|
105
|
+
"#{@field} #{@sequence_number}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def satisfying_value
|
111
|
+
@sequence_number += 1 if includes_uniqueness?
|
112
|
+
value = nil
|
113
|
+
@validations.each do |type, config|
|
114
|
+
case type
|
115
|
+
when :validates_email_format_of
|
116
|
+
value = "john.doe#{@sequence_number}@example.com"
|
117
|
+
when :validates_inclusion_of
|
118
|
+
value = config[:in].first
|
119
|
+
when :validates_presence_of
|
120
|
+
value = satisfying_present_value(value)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
value = unique_value if value.nil? && includes_uniqueness?
|
124
|
+
value
|
125
|
+
end
|
126
|
+
|
127
|
+
def unique_value
|
128
|
+
if column.type == :string
|
129
|
+
"#{@field.to_s.capitalize} #{@sequence_number}"
|
130
|
+
elsif column.type == :datetime
|
131
|
+
Time.utc(1970, 1, 1) + @sequence_number.days
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module SampleModels
|
2
|
+
class Sampler
|
3
|
+
def self.reified_belongs_to_associations(model, attrs)
|
4
|
+
assocs = {}
|
5
|
+
model.belongs_to_associations.each do |assoc|
|
6
|
+
if value = attrs[assoc.name]
|
7
|
+
if value.is_a?(Hash)
|
8
|
+
assocs[assoc.name] = assoc.klass.sample(value)
|
9
|
+
elsif value.is_a?(Array)
|
10
|
+
assocs[assoc.name] = assoc.klass.sample(*value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
assocs
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reify_association_hashes(model, attrs)
|
18
|
+
a = attrs.clone
|
19
|
+
reified_belongs_to_associations(model, attrs).each do |name, value|
|
20
|
+
a[name] = value
|
21
|
+
end
|
22
|
+
model.has_many_associations.each do |assoc|
|
23
|
+
if values = a[assoc.name]
|
24
|
+
a[assoc.name] = values.map { |value|
|
25
|
+
value.is_a?(Hash) ? assoc.klass.sample(value) : value
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
a
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :before_save
|
33
|
+
attr_reader :configured_default_attrs, :model_class, :named_sample_attrs,
|
34
|
+
:polymorphic_default_classes
|
35
|
+
|
36
|
+
def initialize(model_class)
|
37
|
+
@model_class = model_class
|
38
|
+
@configured_default_attrs = {}
|
39
|
+
@named_sample_attrs = HashWithIndifferentAccess.new
|
40
|
+
@polymorphic_default_classes = HashWithIndifferentAccess.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_sample(*args)
|
44
|
+
attrs = preprocessed_attributes(*args)
|
45
|
+
Creation.new(self, attrs).run
|
46
|
+
end
|
47
|
+
|
48
|
+
def fix_deleted_associations(instance, orig_attrs)
|
49
|
+
needs_save = false
|
50
|
+
model.belongs_to_associations.each do |assoc|
|
51
|
+
if instance.send(assoc.primary_key_name) &&
|
52
|
+
!instance.send(assoc.name)
|
53
|
+
instance.send("#{assoc.name}=", assoc.klass.sample)
|
54
|
+
needs_save = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
save!(instance, orig_attrs) if needs_save
|
58
|
+
end
|
59
|
+
|
60
|
+
def model
|
61
|
+
SampleModels.models[@model_class]
|
62
|
+
end
|
63
|
+
|
64
|
+
def preprocessed_attributes(*args)
|
65
|
+
if args.first.is_a?(Symbol)
|
66
|
+
preprocessed_named_sample_attrs(args)
|
67
|
+
else
|
68
|
+
attrs = args.last.is_a?(Hash) ? args.pop : {}
|
69
|
+
args.each do |associated_value|
|
70
|
+
assocs = model.associations(associated_value.class)
|
71
|
+
if assocs.size == 1
|
72
|
+
attrs[assocs.first.name] = associated_value
|
73
|
+
else
|
74
|
+
raise "Not sure what to do with associated value #{associated_value.inspect}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
attrs
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def preprocessed_named_sample_attrs(args)
|
82
|
+
attrs = named_sample_attrs[args.shift]
|
83
|
+
attrs = attrs.merge(args.first) unless args.empty?
|
84
|
+
attrs
|
85
|
+
end
|
86
|
+
|
87
|
+
def sample(*args)
|
88
|
+
attrs = preprocessed_attributes(*args)
|
89
|
+
instance = Finder.new(model, attrs).instance
|
90
|
+
if instance
|
91
|
+
fix_deleted_associations(instance, attrs)
|
92
|
+
else
|
93
|
+
instance = create_sample attrs
|
94
|
+
end
|
95
|
+
instance
|
96
|
+
end
|
97
|
+
|
98
|
+
def save!(instance, orig_attrs)
|
99
|
+
if @before_save
|
100
|
+
if @before_save.arity == 1
|
101
|
+
@before_save.call instance
|
102
|
+
else
|
103
|
+
@before_save.call instance, orig_attrs
|
104
|
+
end
|
105
|
+
end
|
106
|
+
instance.save!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
if RAILS_ENV == 'test' # no reason to run this code outside of test mode
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/sample_models/creation"
|
4
|
+
require "#{File.dirname(__FILE__)}/sample_models/finder"
|
5
|
+
require "#{File.dirname(__FILE__)}/sample_models/model"
|
6
|
+
require "#{File.dirname(__FILE__)}/sample_models/sampler"
|
7
|
+
require "#{File.dirname(__FILE__)}/../vendor/ar_query/lib/ar_query"
|
8
|
+
|
9
|
+
module SampleModels
|
10
|
+
mattr_reader :models
|
11
|
+
@@models = Hash.new { |h, model_class|
|
12
|
+
h[model_class] = Model.new(model_class)
|
13
|
+
}
|
14
|
+
|
15
|
+
mattr_reader :samplers
|
16
|
+
@@samplers = Hash.new { |h, model_class|
|
17
|
+
h[model_class] = Sampler.new(model_class)
|
18
|
+
}
|
19
|
+
|
20
|
+
def self.configure(model_class, opts ={})
|
21
|
+
yield ConfigureRecipient.new(model_class) if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def self.included( mod )
|
27
|
+
mod.extend ARClassMethods
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
class ConfigureRecipient
|
32
|
+
def initialize(model_class)
|
33
|
+
@model_class = model_class
|
34
|
+
end
|
35
|
+
|
36
|
+
def before_save(&proc)
|
37
|
+
sampler.before_save = proc
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(meth, *args)
|
41
|
+
if meth.to_s =~ /(.*)_sample$/
|
42
|
+
sampler.named_sample_attrs[$1] = args.first
|
43
|
+
else
|
44
|
+
Attribute.new(sampler, meth)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def sampler
|
49
|
+
SampleModels.samplers[@model_class]
|
50
|
+
end
|
51
|
+
|
52
|
+
class Attribute
|
53
|
+
def initialize(sampler, attribute)
|
54
|
+
@sampler, @attribute = sampler, attribute
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_class(dc)
|
58
|
+
@sampler.polymorphic_default_classes[@attribute] = dc
|
59
|
+
end
|
60
|
+
|
61
|
+
def default(default)
|
62
|
+
if default.blank? and model.validates_presence_of?(@attribute)
|
63
|
+
raise "#{model.name} requires #{@attribute} to not be blank"
|
64
|
+
else
|
65
|
+
@sampler.configured_default_attrs[@attribute] = default
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def force_unique
|
70
|
+
model.record_validation :validates_uniqueness_of, @attribute
|
71
|
+
end
|
72
|
+
|
73
|
+
def model
|
74
|
+
SampleModels.models[@sampler.model_class]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module ARClassMethods
|
80
|
+
def create_sample(*args)
|
81
|
+
sampler = SampleModels.samplers[self]
|
82
|
+
sampler.create_sample(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def sample(*args)
|
86
|
+
sampler = SampleModels.samplers[self]
|
87
|
+
sampler.sample(*args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module ActiveRecord
|
93
|
+
class Base
|
94
|
+
include SampleModels
|
95
|
+
end
|
96
|
+
|
97
|
+
module Validations
|
98
|
+
module ClassMethods
|
99
|
+
[:validates_email_format_of,
|
100
|
+
:validates_inclusion_of, :validates_presence_of,
|
101
|
+
:validates_uniqueness_of].each do |validation|
|
102
|
+
if method_defined?(validation)
|
103
|
+
define_method "#{validation}_with_sample_models".to_sym do |*args|
|
104
|
+
send "#{validation}_without_sample_models".to_sym, *args
|
105
|
+
SampleModels.models[self].record_validation(
|
106
|
+
validation, *args
|
107
|
+
)
|
108
|
+
end
|
109
|
+
alias_method_chain validation, :sample_models
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end # if RAILS_ENV == 'test'
|
117
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{sample_models}
|
8
|
+
s.version = "0.9.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Francis Hwang"]
|
12
|
+
s.date = %q{2010-11-06}
|
13
|
+
s.description = %q{
|
14
|
+
A library for making it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases. It aims to:
|
15
|
+
|
16
|
+
* meet all your validations automatically
|
17
|
+
* only make you specify the attributes you care about
|
18
|
+
* give you a rich set of features so you can specify associated values as concisely as possible
|
19
|
+
* do this with as little configuration as possible
|
20
|
+
}
|
21
|
+
s.email = %q{francis.hwang@profitably.com}
|
22
|
+
s.extra_rdoc_files = [
|
23
|
+
"README.markdown"
|
24
|
+
]
|
25
|
+
s.files = [
|
26
|
+
".gitignore",
|
27
|
+
"MIT-LICENSE",
|
28
|
+
"README.markdown",
|
29
|
+
"Rakefile",
|
30
|
+
"VERSION",
|
31
|
+
"init.rb",
|
32
|
+
"install.rb",
|
33
|
+
"lib/sample_models.rb",
|
34
|
+
"lib/sample_models/creation.rb",
|
35
|
+
"lib/sample_models/finder.rb",
|
36
|
+
"lib/sample_models/model.rb",
|
37
|
+
"lib/sample_models/sampler.rb",
|
38
|
+
"sample_models.gemspec",
|
39
|
+
"spec/sample_models_spec.rb",
|
40
|
+
"spec_or_test/database.yml",
|
41
|
+
"spec_or_test/setup.rb",
|
42
|
+
"spec_or_test/specs_or_test_cases.rb",
|
43
|
+
"spec_or_test/vendor/validates_email_format_of/CHANGELOG",
|
44
|
+
"spec_or_test/vendor/validates_email_format_of/MIT-LICENSE",
|
45
|
+
"spec_or_test/vendor/validates_email_format_of/README",
|
46
|
+
"spec_or_test/vendor/validates_email_format_of/TODO",
|
47
|
+
"spec_or_test/vendor/validates_email_format_of/init.rb",
|
48
|
+
"spec_or_test/vendor/validates_email_format_of/lib/validates_email_format_of.rb",
|
49
|
+
"spec_or_test/vendor/validates_email_format_of/rakefile",
|
50
|
+
"spec_or_test/vendor/validates_email_format_of/test/database.yml",
|
51
|
+
"spec_or_test/vendor/validates_email_format_of/test/fixtures/people.yml",
|
52
|
+
"spec_or_test/vendor/validates_email_format_of/test/fixtures/person.rb",
|
53
|
+
"spec_or_test/vendor/validates_email_format_of/test/schema.rb",
|
54
|
+
"spec_or_test/vendor/validates_email_format_of/test/test_helper.rb",
|
55
|
+
"spec_or_test/vendor/validates_email_format_of/test/validates_email_format_of_test.rb",
|
56
|
+
"tasks/sample_models_tasks.rake",
|
57
|
+
"test/test_sample_models.rb",
|
58
|
+
"uninstall.rb",
|
59
|
+
"vendor/ar_query/MIT-LICENSE",
|
60
|
+
"vendor/ar_query/README",
|
61
|
+
"vendor/ar_query/ar_query.gemspec",
|
62
|
+
"vendor/ar_query/init.rb",
|
63
|
+
"vendor/ar_query/install.rb",
|
64
|
+
"vendor/ar_query/lib/ar_query.rb",
|
65
|
+
"vendor/ar_query/spec/ar_query_spec.rb",
|
66
|
+
"vendor/ar_query/tasks/ar_query_tasks.rake",
|
67
|
+
"vendor/ar_query/uninstall.rb"
|
68
|
+
]
|
69
|
+
s.homepage = %q{http://github.com/fhwang/sample_models}
|
70
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
71
|
+
s.require_paths = ["lib"]
|
72
|
+
s.rubygems_version = %q{1.3.7}
|
73
|
+
s.summary = %q{A library for making it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases}
|
74
|
+
s.test_files = [
|
75
|
+
"spec/sample_models_spec.rb",
|
76
|
+
"test/test_sample_models.rb"
|
77
|
+
]
|
78
|
+
|
79
|
+
if s.respond_to? :specification_version then
|
80
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
81
|
+
s.specification_version = 3
|
82
|
+
|
83
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
84
|
+
else
|
85
|
+
end
|
86
|
+
else
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_or_test/setup"
|
2
|
+
require 'test/unit/assertions'
|
3
|
+
|
4
|
+
class Spec::Example::ExampleGroup
|
5
|
+
include Test::Unit::Assertions
|
6
|
+
end
|
7
|
+
|
8
|
+
initialize_db
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + "/../spec_or_test/specs_or_test_cases"
|
11
|
+
|