sample_models 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.0
1
+ 2.2.0
@@ -1,187 +1,196 @@
1
1
  module SampleModels
2
- class AttributeSequence
3
- def self.build(*args)
4
- Builder.new(*args).run
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
- def initialize(model, column, validation, input)
8
- @model, @column, @validation, @input = model, column, validation, input
9
- @number = 0
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
- def belongs_to_association
13
- @model.belongs_to_associations.detect { |a|
14
- a.foreign_key == @column.name
15
- }
47
+
48
+ class EmailSource < AbstractSource
49
+ def value
50
+ "john.doe.#{@number}@example.com"
51
+ end
16
52
  end
17
-
18
- def next
19
- @number += 1
20
- @input.next if @input
21
- value
53
+
54
+ class FirstPassBelongsToSource < AbstractSource
55
+ def value
56
+ nil
57
+ end
22
58
  end
23
-
24
- def value
25
- case @column.type
26
- when :string, :text
27
- "#{@column.name} #{@number}"
28
- when :integer
29
- belongs_to_association ? belongs_to_assoc_foreign_key_value : @number
30
- when :datetime
31
- Time.now.utc - @number.minutes
32
- when :date
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 Builder
40
- def initialize(pass, model, column, force_unique, force_email_format)
41
- @pass, @model, @column, @force_unique, @force_email_format =
42
- pass, model, column, force_unique, force_email_format
71
+ class LengthSource < AbstractSource
72
+ def initialize(validation)
73
+ super()
74
+ @validation = validation
43
75
  end
44
-
45
- def base
46
- base_class = SampleModels.const_get(
47
- "#{@pass.to_s.capitalize}PassBaseAttributeSequence"
76
+
77
+ def value
78
+ minimum = @validation.config[:minimum]
79
+ minimum ||= (
80
+ @validation.config[:within] && @validation.config[:within].begin
48
81
  )
49
- base_class.new(@model, @column)
50
- end
51
-
52
- def run
53
- input = base
54
- if @force_email_format
55
- input = ValidatesEmailFormatOfAttributeSequence.new(
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
- if uniqueness_validation
70
- input = ValidatesUniquenessOfAttributeSequence.new(
71
- @model, @column, uniqueness_validation, input
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 sequence_class(validation)
78
- sequence_name = validation.type.to_s.camelize + 'AttributeSequence'
79
- if SampleModels.const_defined?(sequence_name)
80
- SampleModels.const_get(sequence_name)
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
- end
85
-
86
- class FirstPassBaseAttributeSequence < AttributeSequence
87
- def initialize(model, column)
88
- super(model, column, nil, nil)
89
- end
90
-
91
- def belongs_to_assoc_foreign_key_value
92
- nil
93
- end
94
- end
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
- end
120
-
121
- class ValidatesEmailFormatOfAttributeSequence < AttributeSequence
122
- def value
123
- "john.doe.#{@number}@example.com"
124
- end
125
- end
126
-
127
- class ValidatesInclusionOfAttributeSequence < AttributeSequence
128
- def value
129
- @validation.config[:in].first
130
- end
131
- end
132
-
133
- class ValidatesPresenceOfAttributeSequence < AttributeSequence
134
- def belongs_to_value
135
- @previous_belongs_to_instances ||= {}
136
- if @previous_belongs_to_instances[@number]
137
- value = @previous_belongs_to_instances[@number]
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
- def existing_instance_not_previously_returned
152
- previous_ids = @previous_belongs_to_instances.values.map(&:id)
153
- instance = nil
154
- if previous_ids.empty?
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
- end
162
-
163
- def set_belongs_to_instance
164
- instance = existing_instance_not_previously_returned
165
- instance ||= belongs_to_association.klass.sample
166
- @previous_belongs_to_instances[@number] = instance
167
- end
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|
@@ -88,17 +88,16 @@ module SampleModels
88
88
  def initialize(type, config = {})
89
89
  @type, @config = type, config
90
90
  end
91
-
92
- def email_format?
93
- @type == :validates_email_format_of
94
- end
95
-
96
- def inclusion?
97
- @type == :validates_inclusion_of
98
- end
99
-
100
- def presence?
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "sample_models"
8
- s.version = "2.1.0"
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-02"
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|
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + "/../test_helper"
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper'))
2
2
 
3
3
  class BelongsToTest < SampleModelsTestCase
4
4
  def test_associated_with_belongs_to_recipient_by_default
@@ -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)
@@ -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!(:login => 'login 1')
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.1.0
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-02 00:00:00 Z
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: