sample_models 1.2.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,13 +1,8 @@
1
1
  source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
2
 
6
3
  gem 'activerecord', ENV['ACTIVE_RECORD_VERSION']
7
4
  gem "activesupport", ENV['ACTIVE_RECORD_VERSION']
8
5
 
9
- # Add dependencies to develop your gem here.
10
- # Include everything needed to run rake, tests, features, etc.
11
6
  group :development do
12
7
  gem "bundler", "~> 1.0.0"
13
8
  gem "jeweler", "~> 1.6.0"
@@ -15,5 +10,10 @@ end
15
10
 
16
11
  group :test do
17
12
  gem 'sqlite3'
18
- gem "validates_email_format_of"
13
+ version_str = if ENV['ACTIVE_RECORD_VERSION'] =~ /^3\./
14
+ "~> 1.5.0"
15
+ else
16
+ "~> 1.4.0"
17
+ end
18
+ gem "validates_email_format_of", version_str
19
19
  end
data/Gemfile.lock CHANGED
@@ -1,25 +1,40 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activerecord (2.3.10)
5
- activesupport (= 2.3.10)
6
- activesupport (2.3.10)
4
+ activemodel (3.1.0)
5
+ activesupport (= 3.1.0)
6
+ bcrypt-ruby (~> 3.0.0)
7
+ builder (~> 3.0.0)
8
+ i18n (~> 0.6)
9
+ activerecord (3.1.0)
10
+ activemodel (= 3.1.0)
11
+ activesupport (= 3.1.0)
12
+ arel (~> 2.2.1)
13
+ tzinfo (~> 0.3.29)
14
+ activesupport (3.1.0)
15
+ multi_json (~> 1.0)
16
+ arel (2.2.1)
17
+ bcrypt-ruby (3.0.1)
18
+ builder (3.0.0)
7
19
  git (1.2.5)
20
+ i18n (0.6.0)
8
21
  jeweler (1.6.0)
9
22
  bundler (~> 1.0.0)
10
23
  git (>= 1.2.5)
11
24
  rake
25
+ multi_json (1.0.3)
12
26
  rake (0.8.7)
13
27
  sqlite3 (1.3.4)
14
- validates_email_format_of (1.4.5)
28
+ tzinfo (0.3.29)
29
+ validates_email_format_of (1.5.2)
15
30
 
16
31
  PLATFORMS
17
32
  ruby
18
33
 
19
34
  DEPENDENCIES
20
- activerecord (= 2.3.10)
21
- activesupport (= 2.3.10)
35
+ activerecord (= 3.1.0)
36
+ activesupport (= 3.1.0)
22
37
  bundler (~> 1.0.0)
23
38
  jeweler (~> 1.6.0)
24
39
  sqlite3
25
- validates_email_format_of
40
+ validates_email_format_of (~> 1.5.0)
data/README.markdown CHANGED
@@ -8,7 +8,7 @@ A library for making it extremely fast for Rails developers to set up and save A
8
8
  * give you a rich set of features so you can specify associations as concisely as possible
9
9
  * do this with as little configuration as possible
10
10
 
11
- Feature overview
11
+ Overview
12
12
  ================
13
13
 
14
14
  Let's say you've got a set of models that look like this:
@@ -69,6 +69,11 @@ You can specify associated records in the sample call:
69
69
  sad = Tag.sample(:tag => 'sad')
70
70
  funny_yet_sad = BlogPost.sample(:tags => [funny, sad])
71
71
 
72
+ You can also specify associated records by passing them in at the beginning of the argument list, if there's only one association that would work with the record's class:
73
+
74
+ jane = User.sample(:first_name => 'Jane')
75
+ BlogPost.sample(jane, :title => 'What I ate for lunch')
76
+
72
77
  You can also specify associated records by passing in hashes or arrays:
73
78
 
74
79
  bills_post2 = BlogPost.sample(:user => {:first_name => 'Bill'})
@@ -78,11 +83,6 @@ You can also specify associated records by passing in hashes or arrays:
78
83
  :tags => [{:tag => 'funny'}, {:tag => 'sad'}]
79
84
  )
80
85
  puts funny_yet_sad2.tags.size # => 2
81
-
82
- You can also specify associated records by passing them in at the beginning of the argument list, if there's only one association that would work with the record's class:
83
-
84
- jane = User.sample(:first_name => 'Jane')
85
- BlogPost.sample(jane, :title => 'What I ate for lunch')
86
86
 
87
87
  Instance attributes
88
88
  =========================
@@ -94,12 +94,12 @@ SampleModels reads your validations to get hints about how to craft an instance
94
94
  validates_email_format_of
95
95
  -------------------------
96
96
 
97
- If you use the validates_email_format_of plugin at [http://github.com/alexdunae/validates_email_format_of](http://github.com/alexdunae/validates_email_format_of), SampleModels will ensure that the attribute in question is a valid email address.
97
+ If you use the [validates_email_format_of gem](http://rubygems.org/gems/validates_email_format_of), SampleModels will ensure that the attribute in question is a valid email address.
98
98
 
99
99
  validates_presence_of
100
100
  ---------------------
101
101
 
102
- SampleModels already sets values to be non-blank, but this validation comes in handy if you have an `attr_accessor`:
102
+ SampleModels already sets database columns to be non-blank, but this validation comes in handy if you have an `attr_accessor`:
103
103
 
104
104
  class UserWithPassword < ActiveRecord::Base
105
105
  attr_accessor :password
@@ -123,35 +123,6 @@ validates_uniqueness_of
123
123
  SampleModels will ensure that new instances will have different values for attributes where uniqueness is required, as discussed below under "New records vs. old records."
124
124
 
125
125
 
126
- New records vs. old records
127
- ===========================
128
-
129
- Most of the time, consecutive calls to `sample` will return the same record, because this is marginally faster, and the design assumption is that if you're calling `sample` you don't care which instance you get as long as it satisfies the attributes you specified.
130
-
131
- user1 = User.sample
132
- user2 = User.sample
133
- puts (user1 == user2) # probably true
134
-
135
- rick1 = User.sample(:first_name => 'Rick')
136
- puts (user1 == rick1) # probably false, but you never know
137
-
138
- rick2 = User.sample(:first_name => 'Rick')
139
- puts (rick1 == rick2) # probably true
140
-
141
- If having a distinct record is important to the test, you should call `create_sample`, which always saves a new record in the DB and returns it.
142
-
143
- blog_post1 = BlogPost.sample
144
- blog_post2 = BlogPost.create_sample
145
- puts (blog_post1 == blog_post2) # will always be false
146
-
147
- If the class validates the uniqueness of a field, that field will always be distinct for every new instance returned by `create_sample`.
148
-
149
- tag1 = Tag.sample
150
- tag2 = Tag.create_sample
151
- puts (tag1 == tag2) # will always be false
152
- puts (tag1.tag == tag2.tag) # will always be false, because Tag validates
153
- # the uniqueness of the `tag` attribute
154
-
155
126
  Associations
156
127
  ============
157
128
 
@@ -170,7 +141,7 @@ You can also specify these associations as if you were calling `new` or `create!
170
141
  BlogPost.sample(:user => kelley)
171
142
  BlogPost.sample(:user_id => kelley.id)
172
143
 
173
- If you want, you can simply specify the record at the beginning of the argument list for `sample` or `create_sample`, and SampleModels will assign them to the appropriate association, as long as there's only one association that fits the class.
144
+ If you want, you can simply specify the record at the beginning of the argument list for `sample`, and SampleModels will assign them to the appropriate association, as long as there's only one association that fits the class.
174
145
 
175
146
  kim = User.sample(:first_name => 'Kim')
176
147
  BlogPost.sample(kim, :title => 'funny')
@@ -201,8 +172,8 @@ If you want, you can simply specify the important attributes of the associated v
201
172
  You can combine the two syntaxes in deeper associations:
202
173
 
203
174
  bb_episode = Video.sample(:show => [amc, {:name => 'Breaking Bad'}])
204
- puts bb_episode.show.network.name # => 'AMC'
205
- puts bb_episode.show.name # => 'Breaking Bad'
175
+ puts bb_episode.show.network.name # => 'AMC'
176
+ puts bb_episode.show.name # => 'Breaking Bad'
206
177
 
207
178
  Polymorphic belongs-to associations
208
179
  -----------------------------------
@@ -277,7 +248,7 @@ With `before_save` you can specify a block that runs before the record is saved.
277
248
  end
278
249
  end
279
250
 
280
- You can also take a second argument, which will pass in the hash that was used during the call to `sample` or `create_sample`.
251
+ You can also take a second argument, which will pass in the hash that was used during the call to `sample`.
281
252
 
282
253
  SampleModels.configure(Appointment) do |appt|
283
254
  appt.before_save do |appt_record, sample_attrs|
@@ -330,7 +301,7 @@ Named samples can be used to pre-set values for commonly used combinations of at
330
301
  bp1 = BlogPost.sample(:funny)
331
302
  puts bp1.title # => 'Laugh already'
332
303
 
333
- bp2 = BlogPost.create_sample(:funny)
304
+ bp2 = BlogPost.sample(:funny)
334
305
  puts bp2.title # => 'Laugh already'
335
306
  puts (bp1 == bp2) # => false
336
307
 
@@ -338,6 +309,14 @@ You can override individual attributes, as well:
338
309
 
339
310
  bp3 = BlogPost.sample(:funny, :average_rating => 4.0)
340
311
  puts bp3.average_rating # => 4.0
312
+
313
+ Backwards-incompatible changes in SampleModels 2
314
+ ================================================
315
+
316
+ `sample` always creates a new record now. This is a change from SampleModels 1, which would first attempt to find an existing record in the database that satisfied the stated attributes. In practice, that ended up making tests too confusing.
317
+
318
+ `create_sample` and `sample` now do the same thing, and `create_sample` is deprecated.
319
+
341
320
 
342
321
  About
343
322
  =====
data/Rakefile CHANGED
@@ -3,28 +3,24 @@ require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
  require 'rubygems'
5
5
 
6
- ActiveRecordVersions = %w(3.1.1 3.0.1 2.3.10)
6
+ Rake::TestTask.new do |t|
7
+ t.test_files = FileList['test/unit/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ ActiveRecordVersions = %w(3.1.1 3.0.10 2.3.14)
7
12
 
8
- desc "Run all tests"
9
- task :test do
13
+ desc "Run all tests, for all tested versions of ActiveRecord"
14
+ task :all_tests do
10
15
  ActiveRecordVersions.each do |ar_version|
11
- cmd = "ACTIVE_RECORD_VERSION=#{ar_version} ruby test/test_sample_models.rb"
16
+ cmd = "ACTIVE_RECORD_VERSION=#{ar_version} rake test"
12
17
  puts cmd
13
18
  puts `cd . && #{cmd}`
14
19
  puts
15
20
  end
16
21
  end
17
22
 
18
- task :default => :test
19
-
20
- desc 'Generate documentation for the sample_models plugin.'
21
- Rake::RDocTask.new(:rdoc) do |rdoc|
22
- rdoc.rdoc_dir = 'rdoc'
23
- rdoc.title = 'SampleModels'
24
- rdoc.options << '--line-numbers' << '--inline-source'
25
- rdoc.rdoc_files.include('README')
26
- rdoc.rdoc_files.include('lib/**/*.rb')
27
- end
23
+ task :default => :all_tests
28
24
 
29
25
  require 'jeweler'
30
26
  Jeweler::Tasks.new do |gem|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.6
1
+ 2.0.0
@@ -0,0 +1,182 @@
1
+ module SampleModels
2
+ class AttributeSequence
3
+ def self.build(*args)
4
+ Builder.new(*args).run
5
+ end
6
+
7
+ def initialize(model, column, validation, input)
8
+ @model, @column, @validation, @input = model, column, validation, input
9
+ @number = 0
10
+ end
11
+
12
+ def belongs_to_association
13
+ @model.belongs_to_associations.detect { |a|
14
+ a.foreign_key == @column.name
15
+ }
16
+ end
17
+
18
+ def next
19
+ @number += 1
20
+ @input.next if @input
21
+ value
22
+ 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
37
+ end
38
+
39
+ class Builder
40
+ def initialize(pass, model, column, force_unique)
41
+ @pass, @model, @column, @force_unique =
42
+ pass, model, column, force_unique
43
+ end
44
+
45
+ def base
46
+ base_class = SampleModels.const_get(
47
+ "#{@pass.to_s.capitalize}PassBaseAttributeSequence"
48
+ )
49
+ base_class.new(@model, @column)
50
+ end
51
+
52
+ def run
53
+ input = base
54
+ uniqueness_validation = if @force_unique
55
+ Model::Validation.new(:validates_uniqueness_of)
56
+ end
57
+ @model.validations(@column.name).each do |validation|
58
+ if validation.type == :validates_uniqueness_of
59
+ uniqueness_validation = validation
60
+ elsif s_class = sequence_class(validation)
61
+ input = s_class.new(@model, @column, validation, input)
62
+ end
63
+ end
64
+ if uniqueness_validation
65
+ input = ValidatesUniquenessOfAttributeSequence.new(
66
+ @model, @column, uniqueness_validation, input
67
+ )
68
+ end
69
+ input
70
+ end
71
+
72
+ def sequence_class(validation)
73
+ sequence_name = validation.type.to_s.camelize + 'AttributeSequence'
74
+ if SampleModels.const_defined?(sequence_name)
75
+ SampleModels.const_get(sequence_name)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ class FirstPassBaseAttributeSequence < AttributeSequence
82
+ def initialize(model, column)
83
+ super(model, column, nil, nil)
84
+ end
85
+
86
+ def belongs_to_assoc_foreign_key_value
87
+ nil
88
+ end
89
+ end
90
+
91
+ class SecondPassBaseAttributeSequence < AttributeSequence
92
+ def initialize(model, column)
93
+ super(model, column, nil, nil)
94
+ @previous_values = {}
95
+ end
96
+
97
+ def belongs_assoc_value_already_used?(record)
98
+ @previous_values.any? { |prev_num, prev_record|
99
+ prev_record == record && prev_num != @number
100
+ }
101
+ end
102
+
103
+ def belongs_to_assoc_foreign_key_value
104
+ assoc_klass = belongs_to_association.klass
105
+ unless assoc_klass == @model.ar_class
106
+ record = (assoc_klass.last || assoc_klass.sample)
107
+ while belongs_assoc_value_already_used?(record)
108
+ record = assoc_klass.sample
109
+ end
110
+ @previous_values[@number] = record
111
+ record.id
112
+ end
113
+ end
114
+ end
115
+
116
+ class ValidatesEmailFormatOfAttributeSequence < AttributeSequence
117
+ def value
118
+ "john.doe.#{@number}@example.com"
119
+ end
120
+ end
121
+
122
+ class ValidatesInclusionOfAttributeSequence < AttributeSequence
123
+ def value
124
+ @validation.config[:in].first
125
+ end
126
+ end
127
+
128
+ class ValidatesPresenceOfAttributeSequence < AttributeSequence
129
+ def belongs_to_value
130
+ @previous_belongs_to_instances ||= {}
131
+ if @previous_belongs_to_instances[@number]
132
+ value = @previous_belongs_to_instances[@number]
133
+ begin
134
+ value.reload
135
+ value.id
136
+ rescue ActiveRecord::RecordNotFound
137
+ set_belongs_to_instance
138
+ @previous_belongs_to_instances[@number].id
139
+ end
140
+ else
141
+ set_belongs_to_instance
142
+ @previous_belongs_to_instances[@number].id
143
+ end
144
+ end
145
+
146
+ def existing_instance_not_previously_returned
147
+ previous_ids = @previous_belongs_to_instances.values.map(&:id)
148
+ instance = nil
149
+ if previous_ids.empty?
150
+ belongs_to_association.klass.last
151
+ else
152
+ belongs_to_association.klass.last(
153
+ :conditions => ["id not in (?)", previous_ids]
154
+ )
155
+ end
156
+ end
157
+
158
+ def set_belongs_to_instance
159
+ instance = existing_instance_not_previously_returned
160
+ instance ||= belongs_to_association.klass.sample
161
+ @previous_belongs_to_instances[@number] = instance
162
+ end
163
+
164
+ def value
165
+ belongs_to_association ? belongs_to_value : super
166
+ end
167
+ end
168
+
169
+ class ValidatesUniquenessOfAttributeSequence < AttributeSequence
170
+ def value
171
+ v = @input.value
172
+ unless @validation.config[:allow_nil] && v.nil?
173
+ unless @validation.config[:allow_blank] && v.blank?
174
+ until @model.unique?(@column.name, v)
175
+ v = @input.next
176
+ end
177
+ end
178
+ end
179
+ v
180
+ end
181
+ end
182
+ end