modelizer 4.0.0 → 5.0.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.
@@ -1,3 +1,8 @@
1
+ === 5.0.0 / 2011-09-11
2
+
3
+ * Complete breaking change, now provides unified fixtures/factories.
4
+ * Require Ruby 1.9.2 or better.
5
+
1
6
  === 4.0.0 / 2011-06-14
2
7
 
3
8
  * Update for AR 3.1.
@@ -4,7 +4,7 @@ Manifest.txt
4
4
  README.rdoc
5
5
  Rakefile
6
6
  lib/modelizer.rb
7
+ lib/modelizer/all.rb
7
8
  lib/modelizer/assertions.rb
8
9
  lib/modelizer/validations.rb
9
10
  test/test_assertions.rb
10
- test/test_modelizer.rb
@@ -5,72 +5,84 @@
5
5
  == Description
6
6
 
7
7
  Need a simple, consistent way to create model instances and check
8
- validations in your ActiveRecord 3.1+ tests? Use the Modelizer.
8
+ validations in your ActiveRecord 3.1+ tests? Use the Modelizer. Just
9
+ don't trust the docs, since lots changed in 5.x and I got lazy.
9
10
 
10
11
  == Examples
11
12
 
12
- First, enable the Modelizer. I do it in <tt>test/test_helper.rb</tt>:
13
+ First, enable the Modelizer in your test helper. In Rails, try
14
+ something like:
15
+
16
+ require "modelizer/all"
13
17
 
14
18
  class ActiveSupport::TestCase
15
19
  include Modelizer
20
+ extend Modelizer::Validations
16
21
  end
17
22
 
18
- Next, define a model template. I generally do this in the unit test
19
- for the model until I need to use the template somewhere else. As soon
20
- as it's used in multiple places, I move it to a reopened
21
- <tt>ActiveSupport::TestCase</tt> in <tt>test/test_helper.rb</tt>.
23
+ Next, define some fixtures and factories. Modelizer will load these
24
+ when the module is included, using
25
+ <tt>"test/{factories,fixtures}/**/*.rb"</tt> as a default glob. Change
26
+ it by setting <tt>Modelizer.glob</tt> before module inclusion.
27
+
28
+ Fixture and factory definitions look remarkably similar. The only real
29
+ difference is that fixtures get loaded into the DB once at module
30
+ inclusion and factories don't. Factories will also generally use
31
+ autogenerated/random data generators. Some examples:
32
+
33
+ # from test/fixtures/user.rb
22
34
 
23
- class UserTest < ActiveSupport::TestCase
35
+ fixture :user, User do |u|
36
+ u.account = use :account
37
+ u.email = "default@example.org
38
+ u.name = "A User"
39
+ u.password = "123456"
40
+ u.state = "active"
41
+ end
24
42
 
25
- # a simple list of attributes
26
- model_template_for User, :name => "Bob"
27
43
 
28
- # or a block for lazy evaluation
29
- model_template_for User do
30
- { :name => "Bob", :address => addresses(:default) }
31
- end
44
+ # from test/factories/user.rb
32
45
 
46
+ factory :user, User do |u|
47
+ u.account = use :account
48
+ u.email = Faker::Internet.email
49
+ u.name = Faker::Name.name
50
+ u.password = Faker::Lorem.words(1).first
33
51
  end
34
52
 
35
- This declaration generates a bunch of instance methods for you:
53
+ To get a reference to a fixture, the <tt>use</tt> method is added to your
54
+ test class:
55
+
56
+ def test_something
57
+ refute_equal use(:artist).name, use("artist/child").name
58
+ end
36
59
 
37
- * <tt>def valid_user_attributes(extras = {})</tt>
38
- * <tt>def valid_user_attributes_without(*excluded)</tt>
39
- * <tt>def new_user(extras = {})</tt>
40
- * <tt>def new_user_without(*excluded)</tt>
41
- * <tt>def create_user(extras = {})</tt>
42
- * <tt>def create_user!(extras = {})</tt>
43
- * <tt>def create_user_without(*excluded)</tt>
44
- * <tt>def create_user_without!(*excluded)</tt>
60
+ To get an instance from a factory, the <tt>build</tt> and <tt>create</tt> methods
61
+ are available:
45
62
 
46
- It also generates a test to make sure your model template is valid.
63
+ def test_something_else
64
+ assert_equal "foo", build(:artist, name: "foo").name
65
+ refute create(:artist).new_record?
66
+ end
47
67
 
48
68
  === Custom Assertions
49
69
 
50
- Modelizer adds one additional assertion, <tt>assert_invalid</tt>.
70
+ If you require <tt>"modelizer/all"</tt> or include
71
+ <tt>Modelizer::Assertions</tt>, Modelizer adds one additional
72
+ assertion, <tt>assert_invalid</tt>.
51
73
 
52
74
  assert_invalid :email, model, /is bad/
53
75
 
54
76
  The third argument is optional.
55
77
 
56
- === Using in Tests
57
-
58
- class UserTest < ActiveSupport::TestCase
59
- model_template_for User, :name => "Bob"
60
-
61
- def test_pointless_stuff
62
- assert new_user.valid?
63
- assert_invalid :name, new_user_without(:name)
64
-
65
- assert !create_user.new_record?
78
+ def test_pointless_stuff
79
+ assert new_user.valid?
80
+ assert_invalid :name, build(:artist, name: nil)
81
+ end
66
82
 
67
- assert_raise do
68
- create_user_without! :name
69
- end
83
+ === Testing Validations
70
84
 
71
- assert_equal "Fred", new_user(:name => "Fred").name
72
- end
73
- end
85
+ I should really write some docs for this.
74
86
 
75
87
  == Installation
76
88
 
data/Rakefile CHANGED
@@ -10,4 +10,6 @@ Hoe.spec "modelizer" do
10
10
  self.history_file = "CHANGELOG.rdoc"
11
11
  self.readme_file = "README.rdoc"
12
12
  self.testlib = :minitest
13
+
14
+ require_ruby_version ">= 1.9.2"
13
15
  end
@@ -1,138 +1,102 @@
1
- require "modelizer/assertions"
2
- require "modelizer/validations"
1
+ require "zlib"
3
2
 
4
3
  module Modelizer
5
4
 
6
- # Duh.
7
- VERSION = "4.0.0"
5
+ VERSION = "5.0.0"
8
6
 
9
- include Modelizer::Assertions
7
+ def build name, overrides = nil, &block
8
+ model, *initializers = Modelizer.factories[name]
9
+ raise "Can't find the \"#{name}\" factory." unless model
10
10
 
11
- # Test classes that should be considered abstract when rendering
12
- # tests for a model template.
11
+ obj = model.new
13
12
 
14
- TEST_CLASSES = []
13
+ initializers << block if block_given?
14
+ initializers.each { |i| instance_exec obj, &i }
15
15
 
16
- %w(Test::Unit::TestCase Minitest::Unit::TestCase
17
- ActiveSupport::TestCase).each do |k|
16
+ overrides.each { |k, v| obj.send "#{k}=", v } if overrides
17
+
18
+ obj
19
+ end
20
+
21
+ def create name, overrides = nil, &block
22
+ obj = build name, overrides, &block
23
+
24
+ obj.save!
18
25
 
19
- TEST_CLASSES <<
20
- k.split("::").inject(Object) { |a, b| a.const_get b } rescue nil
26
+ obj
21
27
  end
22
28
 
23
- @@cache = {}
24
- def self.cache; @@cache end
29
+ def use name
30
+ model, id = Modelizer.ids[name]
31
+ raise "Can't find the \"#{name}\" fixture." unless model
25
32
 
26
- def self.included target
27
- target.extend ClassMethods
28
- target.extend Modelizer::Validations
33
+ model.find id
29
34
  end
30
35
 
31
- def self.method_name_for model_class
32
- underscore model_class.name
36
+ class Context < Struct.new(:instances)
37
+ def identify name
38
+ Modelizer.identify name
39
+ end
40
+
41
+ def use name
42
+ instances[name] or raise "Can't find the \"#{name}\" fixture."
43
+ end
33
44
  end
34
45
 
35
- def self.model_class_for test_class
36
- test_class.name.gsub(/Test$/, "").constantize
46
+ def self.included klass
47
+ Dir[glob].sort.each { |f| instance_eval File.read(f), f, 1 }
48
+
49
+ instances = {}
50
+ context = Context.new instances
51
+
52
+ fixtures.each do |name, value|
53
+ instances[name] = value.first.new
54
+ end
55
+
56
+ instances.each do |name, obj|
57
+ _, *initializers = fixtures[name]
58
+ initializers.each { |i| context.instance_exec obj, &i }
59
+
60
+ obj.id = identify name
61
+ ids[name] = [obj.class, obj.id]
62
+ end
63
+
64
+ ActiveRecord::Base.transaction do
65
+ instances.each { |_, obj| obj.save! }
66
+ end
37
67
  end
38
68
 
39
- def self.underscore classname
40
- classname.gsub(/::/, '_').
41
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
42
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
43
- tr("-", "_").
44
- downcase
69
+ class << self
70
+ attr_accessor :glob
45
71
  end
46
72
 
47
- def assign_model_template_attributes model, attributes
48
- model.assign_attributes attributes, without_protection: true
49
- model
73
+ self.glob = "test/{factories,fixtures}/**/*.rb"
74
+
75
+ def self.cache
76
+ @cache ||= {}
50
77
  end
51
78
 
52
- def valid_model_template_attributes klass, extras = {}
53
- defaults, block = ::Modelizer.cache[klass]
54
- lazy = block && instance_eval(&block)
55
- [defaults, lazy, extras].compact.inject { |t, s| t.merge s }
79
+ def self.factory name, model, &initializer
80
+ factories[name] = [model, initializer]
56
81
  end
57
82
 
58
- def valid_model_template_attributes_without klass, excluded
59
- valid_model_template_attributes(klass).delete_if do |k, v|
60
- excluded.include? k
61
- end
83
+ def self.factories
84
+ @factories ||= {}
62
85
  end
63
86
 
64
- module ClassMethods
65
- def model_template_for klass, defaults = {}, &block
66
- if defaults.nil? && !block
67
- raise ArgumentError, "default attributes or lazy block required"
68
- end
69
-
70
- ::Modelizer.cache[klass] = [defaults, block]
71
-
72
- model = ::Modelizer.method_name_for klass
73
- klass = klass.name
74
-
75
- module_eval <<-END, __FILE__, __LINE__ + 1
76
- def valid_#{model}_attributes extras = {}
77
- valid_model_template_attributes #{klass}, extras
78
- end
79
-
80
- def valid_#{model}_attributes_without *excluded
81
- valid_model_template_attributes_without #{klass}, excluded
82
- end
83
-
84
- def new_#{model} extras = {}
85
- assign_model_template_attributes #{klass}.new,
86
- valid_model_template_attributes(#{klass}, extras)
87
- end
88
-
89
- def new_#{model}_without *excluded
90
- assign_model_template_attributes #{klass}.new,
91
- valid_model_template_attributes_without(#{klass}, excluded)
92
- end
93
-
94
- def create_#{model} extras = {}
95
- (m = new_#{model}(extras)).save; m
96
- end
97
-
98
- def create_#{model}! extras = {}
99
- (m = new_#{model}(extras)).save!; m
100
- end
101
-
102
- def create_#{model}_without *excluded
103
- (m = new_#{model}_without(*excluded)).save; m
104
- end
105
-
106
- def create_#{model}_without! *excluded
107
- (m = new_#{model}_without(*excluded)).save!; m
108
- end
109
- END
110
-
111
- # Install a test that ensures the model template is valid. If
112
- # the template is defined in one of the abstract test
113
- # superclasses, generate a whole new testcase. If it's in a
114
- # concrete test, just generate a method.
115
-
116
- file, line = caller.first.split ":"
117
- line = line.to_i
118
-
119
- test = <<-END
120
- def test_model_template_for_#{model}
121
- assert (m = new_#{model}).valid?,
122
- "#{klass} template is invalid: " +
123
- m.errors.full_messages.to_sentence
124
- end
125
- END
126
-
127
- if TEST_CLASSES.include? self
128
- eval <<-END, nil, file, line - 2
129
- class ::ModelTemplateFor#{klass}Test < ActiveSupport::TestCase
130
- #{test}
131
- end
132
- END
133
- else
134
- module_eval test, file, line - 1
135
- end
136
- end
87
+ def self.fixture name, model, &initializer
88
+ fixtures[name] = [model, initializer]
89
+ end
90
+
91
+ def self.fixtures
92
+ @fixtures ||= {}
93
+ end
94
+
95
+ def self.identify name
96
+ Zlib.crc32(name.to_s) % (2 ** 30 - 1)
97
+ end
98
+
99
+ def self.ids
100
+ @ids ||= {}
137
101
  end
138
102
  end
@@ -0,0 +1,3 @@
1
+ require "modelizer"
2
+ require "modelizer/assertions"
3
+ require "modelizer/validations"
@@ -13,4 +13,6 @@ module Modelizer
13
13
  assert_match match, model.errors.on(attribute) if match
14
14
  end
15
15
  end
16
+
17
+ include Assertions
16
18
  end
@@ -1,36 +1,23 @@
1
1
  module Modelizer
2
2
  module Validations
3
- def test_validations_for attribute, *validations
4
- @klass ||= ::Modelizer.model_class_for self
5
- @model ||= ::Modelizer.method_name_for @klass
6
-
7
- unless instance_methods.collect { |m| m.to_s }.include? "new_#{@model}"
8
- raise "no model template for #{@klass.name}"
9
- end
10
-
11
- # FIX: location in original test file
12
-
13
- validations.each do |v|
14
- test = send "validation_lambda_for_#{v}", @klass, @model, attribute
15
- define_method "test_#{attribute}_#{v}", &test
3
+ def test_presence_for plan, attribute
4
+ define_method "test_#{attribute}_presence" do
5
+ bad = build plan, attribute => nil
6
+ assert_invalid attribute, bad
16
7
  end
17
8
  end
18
9
 
19
- private
20
-
21
- def validation_lambda_for_presence klass, model, attribute
22
- lambda do
23
- assert_invalid attribute, send("new_#{model}", attribute => nil)
10
+ def test_uniqueness_for plan, attribute
11
+ define_method "test_#{attribute}_uniqueness" do
12
+ good = create plan
13
+ bad = build(plan) { |o| o.send("#{attribute}=", good.send(attribute)) }
14
+ assert_invalid attribute, bad
24
15
  end
25
16
  end
26
17
 
27
- def validation_lambda_for_uniqueness klass, model, attribute
28
- lambda do
29
- existing = klass.first
30
- assert existing, "There's at least one #{model} fixture."
31
-
32
- assert_invalid attribute,
33
- send("new_#{model}", attribute => existing.send(attribute))
18
+ def test_validations_for plan, attribute, *validations
19
+ validations.each do |validation|
20
+ send "test_#{validation}_for", plan, attribute
34
21
  end
35
22
  end
36
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modelizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,23 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-14 00:00:00.000000000 -05:00
13
- default_executable:
12
+ date: 2011-09-11 00:00:00.000000000Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: hoe
17
- requirement: &2151797440 !ruby/object:Gem::Requirement
16
+ requirement: &70235549949760 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
- - - ! '>='
19
+ - - ~>
21
20
  - !ruby/object:Gem::Version
22
- version: 2.9.4
21
+ version: '2.12'
23
22
  type: :development
24
23
  prerelease: false
25
- version_requirements: *2151797440
24
+ version_requirements: *70235549949760
26
25
  description: ! 'Need a simple, consistent way to create model instances and check
27
26
 
28
- validations in your ActiveRecord 3.1+ tests? Use the Modelizer.'
27
+ validations in your ActiveRecord 3.1+ tests? Use the Modelizer. Just
28
+
29
+ don''t trust the docs, since lots changed in 5.x and I got lazy.'
29
30
  email:
30
31
  - code@jbarnette.com
31
32
  executables: []
@@ -41,12 +42,11 @@ files:
41
42
  - README.rdoc
42
43
  - Rakefile
43
44
  - lib/modelizer.rb
45
+ - lib/modelizer/all.rb
44
46
  - lib/modelizer/assertions.rb
45
47
  - lib/modelizer/validations.rb
46
48
  - test/test_assertions.rb
47
- - test/test_modelizer.rb
48
49
  - .gemtest
49
- has_rdoc: true
50
50
  homepage: http://github.com/jbarnette/modelizer
51
51
  licenses: []
52
52
  post_install_message:
@@ -60,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - ! '>='
62
62
  - !ruby/object:Gem::Version
63
- version: '0'
63
+ version: 1.9.2
64
64
  required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
@@ -69,11 +69,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  version: '0'
70
70
  requirements: []
71
71
  rubyforge_project: modelizer
72
- rubygems_version: 1.6.2
72
+ rubygems_version: 1.8.7
73
73
  signing_key:
74
74
  specification_version: 3
75
75
  summary: Need a simple, consistent way to create model instances and check validations
76
- in your ActiveRecord 3.1+ tests? Use the Modelizer.
76
+ in your ActiveRecord 3.1+ tests? Use the Modelizer
77
77
  test_files:
78
78
  - test/test_assertions.rb
79
- - test/test_modelizer.rb
@@ -1,14 +0,0 @@
1
- require "minitest/autorun"
2
- require "modelizer"
3
-
4
- class TestModelizer < MiniTest::Unit::TestCase
5
- def setup
6
- @klass = Class.new
7
- @klass.send :include, Modelizer
8
- end
9
-
10
- def test_adds_model_template_for_class_method
11
- assert_includes @klass.singleton_methods.collect { |m| m.to_s },
12
- "model_template_for"
13
- end
14
- end