sample_models 2.1.0 → 2.2.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/README.markdown +7 -0
- data/VERSION +1 -1
- data/lib/sample_models/attribute_sequence.rb +167 -158
- data/lib/sample_models/initializer.rb +1 -1
- data/lib/sample_models/model.rb +10 -11
- data/sample_models.gemspec +3 -2
- data/test/setup/models.rb +5 -0
- data/test/setup/schema.rb +4 -0
- data/test/unit/belongs_to_test.rb +1 -1
- data/test/unit/configuration_test.rb +0 -7
- data/test/unit/sample_test.rb +4 -21
- data/test/unit/validations_test.rb +44 -0
- metadata +3 -2
data/README.markdown
CHANGED
@@ -117,6 +117,13 @@ validates_inclusion_of
|
|
117
117
|
SampleModels will set the attribute to one of the specified values.
|
118
118
|
|
119
119
|
|
120
|
+
validates_length_of
|
121
|
+
-------------------
|
122
|
+
|
123
|
+
SampleModels will set the attribute to a string within the specified
|
124
|
+
length constraints.
|
125
|
+
|
126
|
+
|
120
127
|
validates_uniqueness_of
|
121
128
|
-----------------------
|
122
129
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
@@ -1,187 +1,196 @@
|
|
1
1
|
module SampleModels
|
2
|
-
|
3
|
-
def self.build(
|
4
|
-
|
2
|
+
module AttributeSequence
|
3
|
+
def self.build(pass, model, column, force_unique, force_email_format)
|
4
|
+
sequence = source(pass, model, column, force_email_format)
|
5
|
+
validations = model.validations(column.name)
|
6
|
+
if (v = validations.detect(&:uniqueness?)) || force_unique
|
7
|
+
v ||= Model::Validation.new(:validates_uniqueness_of)
|
8
|
+
sequence = UniquenessFilter.new(model, column, v, sequence)
|
9
|
+
end
|
10
|
+
sequence
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.source(pass, model, column, force_email_format)
|
14
|
+
validations = model.validations(column.name)
|
15
|
+
belongs_to_assocs = model.belongs_to_associations
|
16
|
+
if force_email_format
|
17
|
+
EmailSource.new
|
18
|
+
elsif assoc = belongs_to_assocs.detect { |a| a.foreign_key == column.name }
|
19
|
+
if validations.any?(&:presence?)
|
20
|
+
RequiredBelongsToSource.new(assoc)
|
21
|
+
elsif pass == :first
|
22
|
+
FirstPassBelongsToSource.new
|
23
|
+
else
|
24
|
+
SecondPassBelongsToSource.new(model, assoc)
|
25
|
+
end
|
26
|
+
elsif validations.any?(&:email_format?)
|
27
|
+
EmailSource.new
|
28
|
+
elsif v = validations.detect(&:inclusion?)
|
29
|
+
InclusionSource.new(v)
|
30
|
+
elsif v = validations.detect(&:length?)
|
31
|
+
LengthSource.new(v)
|
32
|
+
else
|
33
|
+
SimpleSource.new(column)
|
34
|
+
end
|
5
35
|
end
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
36
|
+
|
37
|
+
class AbstractSource
|
38
|
+
def initialize
|
39
|
+
@number = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def next
|
43
|
+
@number += 1
|
44
|
+
value
|
45
|
+
end
|
10
46
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
47
|
+
|
48
|
+
class EmailSource < AbstractSource
|
49
|
+
def value
|
50
|
+
"john.doe.#{@number}@example.com"
|
51
|
+
end
|
16
52
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
53
|
+
|
54
|
+
class FirstPassBelongsToSource < AbstractSource
|
55
|
+
def value
|
56
|
+
nil
|
57
|
+
end
|
22
58
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
Date.today - @number
|
34
|
-
when :float
|
35
|
-
@number.to_f
|
36
|
-
end
|
59
|
+
|
60
|
+
class InclusionSource < AbstractSource
|
61
|
+
def initialize(validation)
|
62
|
+
super()
|
63
|
+
@validation = validation
|
64
|
+
end
|
65
|
+
|
66
|
+
def value
|
67
|
+
@validation.config[:in].first
|
68
|
+
end
|
37
69
|
end
|
38
70
|
|
39
|
-
class
|
40
|
-
def initialize(
|
41
|
-
|
42
|
-
|
71
|
+
class LengthSource < AbstractSource
|
72
|
+
def initialize(validation)
|
73
|
+
super()
|
74
|
+
@validation = validation
|
43
75
|
end
|
44
|
-
|
45
|
-
def
|
46
|
-
|
47
|
-
|
76
|
+
|
77
|
+
def value
|
78
|
+
minimum = @validation.config[:minimum]
|
79
|
+
minimum ||= (
|
80
|
+
@validation.config[:within] && @validation.config[:within].begin
|
48
81
|
)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@model, @column, nil, input
|
57
|
-
)
|
58
|
-
end
|
59
|
-
uniqueness_validation = if @force_unique
|
60
|
-
Model::Validation.new(:validates_uniqueness_of)
|
61
|
-
end
|
62
|
-
@model.validations(@column.name).each do |validation|
|
63
|
-
if validation.type == :validates_uniqueness_of
|
64
|
-
uniqueness_validation = validation
|
65
|
-
elsif s_class = sequence_class(validation)
|
66
|
-
input = s_class.new(@model, @column, validation, input)
|
67
|
-
end
|
82
|
+
minimum ||= (
|
83
|
+
@validation.config[:in] && @validation.config[:in].begin
|
84
|
+
)
|
85
|
+
minimum ||= 1
|
86
|
+
value = 'a' * minimum
|
87
|
+
@number.times do
|
88
|
+
value = value.succ
|
68
89
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
90
|
+
value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class RequiredBelongsToSource < AbstractSource
|
95
|
+
def initialize(assoc)
|
96
|
+
super()
|
97
|
+
@assoc = assoc
|
98
|
+
@previous_instances = {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def existing_instance_not_previously_returned
|
102
|
+
previous_ids = @previous_instances.values.map(&:id)
|
103
|
+
instance = nil
|
104
|
+
if previous_ids.empty?
|
105
|
+
@assoc.klass.last
|
106
|
+
else
|
107
|
+
@assoc.klass.last(
|
108
|
+
:conditions => ["id not in (?)", previous_ids]
|
72
109
|
)
|
73
110
|
end
|
74
|
-
input
|
75
111
|
end
|
76
|
-
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
112
|
+
|
113
|
+
def set_instance
|
114
|
+
instance = existing_instance_not_previously_returned
|
115
|
+
instance ||= @assoc.klass.sample
|
116
|
+
@previous_instances[@number] = instance
|
117
|
+
end
|
118
|
+
|
119
|
+
def value
|
120
|
+
if @previous_instances[@number]
|
121
|
+
value = @previous_instances[@number]
|
122
|
+
begin
|
123
|
+
value.reload
|
124
|
+
value.id
|
125
|
+
rescue ActiveRecord::RecordNotFound
|
126
|
+
set_instance
|
127
|
+
@previous_instances[@number].id
|
128
|
+
end
|
129
|
+
else
|
130
|
+
set_instance
|
131
|
+
@previous_instances[@number].id
|
81
132
|
end
|
82
133
|
end
|
83
134
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
class SecondPassBaseAttributeSequence < AttributeSequence
|
97
|
-
def initialize(model, column)
|
98
|
-
super(model, column, nil, nil)
|
99
|
-
@previous_values = {}
|
100
|
-
end
|
101
|
-
|
102
|
-
def belongs_assoc_value_already_used?(record)
|
103
|
-
@previous_values.any? { |prev_num, prev_record|
|
104
|
-
prev_record == record && prev_num != @number
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
def belongs_to_assoc_foreign_key_value
|
109
|
-
assoc_klass = belongs_to_association.klass
|
110
|
-
unless assoc_klass == @model.ar_class
|
111
|
-
record = (assoc_klass.last || assoc_klass.sample)
|
112
|
-
while belongs_assoc_value_already_used?(record)
|
113
|
-
record = assoc_klass.sample
|
135
|
+
|
136
|
+
class SecondPassBelongsToSource < AbstractSource
|
137
|
+
def initialize(model, assoc)
|
138
|
+
super()
|
139
|
+
@model, @assoc = model, assoc
|
140
|
+
end
|
141
|
+
|
142
|
+
def value
|
143
|
+
assoc_klass = @assoc.klass
|
144
|
+
unless assoc_klass == @model.ar_class
|
145
|
+
record = (assoc_klass.last || assoc_klass.sample)
|
146
|
+
record.id
|
114
147
|
end
|
115
|
-
@previous_values[@number] = record
|
116
|
-
record.id
|
117
148
|
end
|
118
149
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
begin
|
139
|
-
value.reload
|
140
|
-
value.id
|
141
|
-
rescue ActiveRecord::RecordNotFound
|
142
|
-
set_belongs_to_instance
|
143
|
-
@previous_belongs_to_instances[@number].id
|
150
|
+
|
151
|
+
class SimpleSource < AbstractSource
|
152
|
+
def initialize(column)
|
153
|
+
super()
|
154
|
+
@column = column
|
155
|
+
end
|
156
|
+
|
157
|
+
def value
|
158
|
+
case @column.type
|
159
|
+
when :string, :text
|
160
|
+
"#{@column.name} #{@number}"
|
161
|
+
when :integer
|
162
|
+
@number
|
163
|
+
when :datetime
|
164
|
+
Time.now.utc - @number.minutes
|
165
|
+
when :date
|
166
|
+
Date.today - @number
|
167
|
+
when :float
|
168
|
+
@number.to_f
|
144
169
|
end
|
145
|
-
else
|
146
|
-
set_belongs_to_instance
|
147
|
-
@previous_belongs_to_instances[@number].id
|
148
170
|
end
|
149
171
|
end
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
belongs_to_association.klass.last
|
156
|
-
else
|
157
|
-
belongs_to_association.klass.last(
|
158
|
-
:conditions => ["id not in (?)", previous_ids]
|
159
|
-
)
|
172
|
+
|
173
|
+
class UniquenessFilter
|
174
|
+
def initialize(model, column, validation, input)
|
175
|
+
@model, @column, @validation, @input =
|
176
|
+
model, column, validation, input
|
160
177
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
def value
|
170
|
-
belongs_to_association ? belongs_to_value : super
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
class ValidatesUniquenessOfAttributeSequence < AttributeSequence
|
175
|
-
def value
|
176
|
-
v = @input.value
|
177
|
-
unless @validation.config[:allow_nil] && v.nil?
|
178
|
-
unless @validation.config[:allow_blank] && v.blank?
|
179
|
-
until @model.unique?(@column.name, v)
|
180
|
-
v = @input.next
|
178
|
+
|
179
|
+
def next
|
180
|
+
v = @input.next
|
181
|
+
unless @validation.config[:allow_nil] && v.nil?
|
182
|
+
unless @validation.config[:allow_blank] && v.blank?
|
183
|
+
until @model.unique?(@column.name, v)
|
184
|
+
v = @input.next
|
185
|
+
end
|
181
186
|
end
|
182
187
|
end
|
188
|
+
v
|
189
|
+
end
|
190
|
+
|
191
|
+
def value
|
192
|
+
self.next
|
183
193
|
end
|
184
|
-
v
|
185
194
|
end
|
186
195
|
end
|
187
196
|
end
|
@@ -17,7 +17,7 @@ module SampleModels
|
|
17
17
|
def intercept_validation_definitions
|
18
18
|
validations_to_intercept = [
|
19
19
|
:validates_email_format_of, :validates_inclusion_of,
|
20
|
-
:validates_presence_of, :validates_uniqueness_of
|
20
|
+
:validates_length_of, :validates_presence_of, :validates_uniqueness_of
|
21
21
|
]
|
22
22
|
optional_interceptions = [:validates_email_format_of]
|
23
23
|
validations_to_intercept.each do |validation|
|
data/lib/sample_models/model.rb
CHANGED
@@ -88,17 +88,16 @@ module SampleModels
|
|
88
88
|
def initialize(type, config = {})
|
89
89
|
@type, @config = type, config
|
90
90
|
end
|
91
|
-
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
@type == :validates_presence_of
|
91
|
+
|
92
|
+
def method_missing(meth, *args, &block)
|
93
|
+
type_predicates = %w(
|
94
|
+
email_format? inclusion? length? presence? uniqueness?
|
95
|
+
)
|
96
|
+
if type_predicates.include?(meth.to_s)
|
97
|
+
@type == "validates_#{meth.to_s.chop}_of".to_sym
|
98
|
+
else
|
99
|
+
super
|
100
|
+
end
|
102
101
|
end
|
103
102
|
end
|
104
103
|
end
|
data/sample_models.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "sample_models"
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Francis Hwang"]
|
12
|
-
s.date = "2012-03-
|
12
|
+
s.date = "2012-03-04"
|
13
13
|
s.description = "\nA library for making it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases. It aims to:\n\n* meet all your validations automatically\n* only make you specify the attributes you care about\n* give you a rich set of features so you can specify associated values as concisely as possible\n* do this with as little configuration as possible\n"
|
14
14
|
s.email = "francis.hwang@profitably.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -43,6 +43,7 @@ Gem::Specification.new do |s|
|
|
43
43
|
"test/unit/named_sample_test.rb",
|
44
44
|
"test/unit/polymorphic_belongs_to_test.rb",
|
45
45
|
"test/unit/sample_test.rb",
|
46
|
+
"test/unit/validations_test.rb",
|
46
47
|
"uninstall.rb"
|
47
48
|
]
|
48
49
|
s.homepage = "http://github.com/fhwang/sample_models"
|
data/test/setup/models.rb
CHANGED
@@ -99,6 +99,11 @@ end
|
|
99
99
|
class User2 < ActiveRecord::Base
|
100
100
|
validates_presence_of :login
|
101
101
|
validates_uniqueness_of :login
|
102
|
+
validates_length_of :token1, :minimum => 40
|
103
|
+
validates_length_of :token2, :maximum => 4
|
104
|
+
validates_uniqueness_of :token2
|
105
|
+
validates_length_of :token3, :within => (20..40)
|
106
|
+
validates_length_of :token4, :in => (20..40)
|
102
107
|
end
|
103
108
|
|
104
109
|
class UserWithPassword < ActiveRecord::Base
|
data/test/setup/schema.rb
CHANGED
@@ -89,6 +89,10 @@ silence_stream(STDOUT) do
|
|
89
89
|
create_table 'user2s', :force => true do |user2|
|
90
90
|
user2.string 'login'
|
91
91
|
user2.string 'email'
|
92
|
+
user2.string 'token1'
|
93
|
+
user2.string 'token2'
|
94
|
+
user2.string 'token3'
|
95
|
+
user2.string 'token4'
|
92
96
|
end
|
93
97
|
|
94
98
|
create_table 'user_with_passwords', :force => true do |user|
|
@@ -73,13 +73,6 @@ class ConfigurationTest < SampleModelsTestCase
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
def test_validate_uniqueness_with_an_allow_nil_allows_nil_configuration
|
77
|
-
User.sample
|
78
|
-
user = User.sample
|
79
|
-
assert_nil user.external_user
|
80
|
-
assert_nil user.external_user_id
|
81
|
-
end
|
82
|
-
|
83
76
|
def test_attr_accessor_can_have_configured_default
|
84
77
|
blog_post = BlogPost.sample
|
85
78
|
assert_equal('I am an instance attribute', blog_post.instance_attribute)
|
data/test/unit/sample_test.rb
CHANGED
@@ -31,26 +31,6 @@ class SampleTest < SampleModelsTestCase
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def test_picks_a_value_given_in_a_validates_inclusion_of
|
35
|
-
assert_difference('User.count') do
|
36
|
-
user = User.sample
|
37
|
-
assert(%(m f).include?(user.gender))
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_cant_override_validations
|
42
|
-
assert_no_difference('User.count') do
|
43
|
-
assert_raise(ActiveRecord::RecordInvalid) do
|
44
|
-
User.sample(:gender => 'x')
|
45
|
-
end
|
46
|
-
end
|
47
|
-
assert_no_difference('User.count') do
|
48
|
-
assert_raise(ActiveRecord::RecordInvalid) do
|
49
|
-
User.sample(:email => 'call.me')
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
34
|
def test_set_emails
|
55
35
|
assert_difference('User.count') do
|
56
36
|
user = User.sample
|
@@ -110,7 +90,10 @@ class SampleTest < SampleModelsTestCase
|
|
110
90
|
def test_string_which_is_required_to_be_present_and_unique
|
111
91
|
# Ensuring that it doesn't get tripped up by a pre-existing record
|
112
92
|
User2.destroy_all
|
113
|
-
User2.create!(
|
93
|
+
User2.create!(
|
94
|
+
:login => 'login 1', :token1 => 'a' * 40, :token2 => 'a' * 4,
|
95
|
+
:token3 => 'a' * 20, :token4 => 'a' * 20
|
96
|
+
)
|
114
97
|
User2.sample
|
115
98
|
end
|
116
99
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper'))
|
2
|
+
|
3
|
+
class ValidationsTest < SampleModelsTestCase
|
4
|
+
def test_validate_uniqueness_with_an_allow_nil_allows_nil_configuration
|
5
|
+
User.sample
|
6
|
+
user = User.sample
|
7
|
+
assert_nil user.external_user
|
8
|
+
assert_nil user.external_user_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_picks_a_value_given_in_a_validates_inclusion_of
|
12
|
+
assert_difference('User.count') do
|
13
|
+
user = User.sample
|
14
|
+
assert(%(m f).include?(user.gender))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_cant_override_validations
|
19
|
+
assert_no_difference('User.count') do
|
20
|
+
assert_raise(ActiveRecord::RecordInvalid) do
|
21
|
+
User.sample(:gender => 'x')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
assert_no_difference('User.count') do
|
25
|
+
assert_raise(ActiveRecord::RecordInvalid) do
|
26
|
+
User.sample(:email => 'call.me')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_validates_length_of
|
32
|
+
token2s = []
|
33
|
+
50.times do
|
34
|
+
user2 = User2.sample
|
35
|
+
assert(user2.token1.length >= 40)
|
36
|
+
assert(user2.token2.length <= 4)
|
37
|
+
token2s << user2.token2
|
38
|
+
assert(user2.token3.length >= 20 && user2.token3.length <= 40)
|
39
|
+
assert(user2.token4.length >= 20 && user2.token4.length <= 40)
|
40
|
+
end
|
41
|
+
assert_equal(token2s.size, token2s.uniq.size)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: sample_models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.
|
5
|
+
version: 2.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Francis Hwang
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-03-
|
13
|
+
date: 2012-03-04 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- test/unit/named_sample_test.rb
|
98
98
|
- test/unit/polymorphic_belongs_to_test.rb
|
99
99
|
- test/unit/sample_test.rb
|
100
|
+
- test/unit/validations_test.rb
|
100
101
|
- uninstall.rb
|
101
102
|
homepage: http://github.com/fhwang/sample_models
|
102
103
|
licenses:
|