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