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 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: