JamieFlournoy-machinist 1.0.6
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/.autotest +7 -0
- data/.gitignore +4 -0
- data/FAQ.markdown +18 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +319 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/init.rb +2 -0
- data/lib/machinist.rb +121 -0
- data/lib/machinist/active_record.rb +99 -0
- data/lib/machinist/blueprints.rb +25 -0
- data/lib/machinist/data_mapper.rb +83 -0
- data/lib/machinist/object.rb +30 -0
- data/lib/machinist/sequel.rb +62 -0
- data/lib/sham.rb +81 -0
- data/machinist.gemspec +90 -0
- data/spec/active_record_spec.rb +200 -0
- data/spec/data_mapper_spec.rb +136 -0
- data/spec/db/.gitignore +1 -0
- data/spec/db/schema.rb +20 -0
- data/spec/log/.gitignore +1 -0
- data/spec/machinist_spec.rb +208 -0
- data/spec/sequel_spec.rb +151 -0
- data/spec/sham_spec.rb +95 -0
- data/spec/spec_helper.rb +9 -0
- metadata +177 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'machinist/blueprints'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Machinist
|
6
|
+
|
7
|
+
class ActiveRecordAdapter
|
8
|
+
|
9
|
+
def self.has_association?(object, attribute)
|
10
|
+
object.class.reflect_on_association(attribute)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.class_for_association(object, attribute)
|
14
|
+
association = object.class.reflect_on_association(attribute)
|
15
|
+
association && association.klass
|
16
|
+
end
|
17
|
+
|
18
|
+
# This method takes care of converting any associated objects,
|
19
|
+
# in the hash returned by Lathe#assigned_attributes, into their
|
20
|
+
# object ids.
|
21
|
+
#
|
22
|
+
# For example, let's say we have blueprints like this:
|
23
|
+
#
|
24
|
+
# Post.blueprint { }
|
25
|
+
# Comment.blueprint { post }
|
26
|
+
#
|
27
|
+
# Lathe#assigned_attributes will return { :post => ... }, but
|
28
|
+
# we want to pass { :post_id => 1 } to a controller.
|
29
|
+
#
|
30
|
+
# This method takes care of cleaning this up.
|
31
|
+
def self.assigned_attributes_without_associations(lathe)
|
32
|
+
attributes = {}
|
33
|
+
lathe.assigned_attributes.each_pair do |attribute, value|
|
34
|
+
association = lathe.object.class.reflect_on_association(attribute)
|
35
|
+
if association && association.macro == :belongs_to && !value.nil?
|
36
|
+
attributes[association.primary_key_name.to_sym] = value.id
|
37
|
+
else
|
38
|
+
attributes[attribute] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
module ActiveRecordExtensions
|
47
|
+
def self.included(base)
|
48
|
+
base.extend(ClassMethods)
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
def make(*args, &block)
|
53
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.new, *args)
|
54
|
+
unless Machinist.nerfed?
|
55
|
+
lathe.object.save!
|
56
|
+
lathe.object.reload
|
57
|
+
end
|
58
|
+
lathe.object(&block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def make_unsaved(*args)
|
62
|
+
object = Machinist.with_save_nerfed { make(*args) }
|
63
|
+
yield object if block_given?
|
64
|
+
object
|
65
|
+
end
|
66
|
+
|
67
|
+
def plan(*args)
|
68
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.new, *args)
|
69
|
+
Machinist::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module ActiveRecordHasManyExtensions
|
75
|
+
def make(*args, &block)
|
76
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.build, *args)
|
77
|
+
unless Machinist.nerfed?
|
78
|
+
lathe.object.save!
|
79
|
+
lathe.object.reload
|
80
|
+
end
|
81
|
+
lathe.object(&block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def plan(*args)
|
85
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.build, *args)
|
86
|
+
Machinist::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
class ActiveRecord::Base
|
93
|
+
include Machinist::Blueprints
|
94
|
+
include Machinist::ActiveRecordExtensions
|
95
|
+
end
|
96
|
+
|
97
|
+
class ActiveRecord::Associations::HasManyAssociation
|
98
|
+
include Machinist::ActiveRecordHasManyExtensions
|
99
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Machinist
|
2
|
+
# Include this in a class to allow defining blueprints for that class.
|
3
|
+
module Blueprints
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def blueprint(name = :master, &blueprint)
|
10
|
+
@blueprints ||= {}
|
11
|
+
@blueprints[name] = blueprint if block_given?
|
12
|
+
@blueprints[name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def named_blueprints
|
16
|
+
@blueprints.reject{|name,_| name == :master }.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear_blueprints!
|
20
|
+
@blueprints = {}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'machinist/blueprints'
|
3
|
+
require 'dm-core'
|
4
|
+
|
5
|
+
module Machinist
|
6
|
+
|
7
|
+
class DataMapperAdapter
|
8
|
+
def self.has_association?(object, attribute)
|
9
|
+
object.class.relationships.has_key?(attribute)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.class_for_association(object, attribute)
|
13
|
+
association = object.class.relationships[attribute]
|
14
|
+
association && association.parent_model
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.association_is_many_to_one?(association)
|
18
|
+
if defined?(DataMapper::Associations::ManyToOne::Relationship)
|
19
|
+
# We're using the next branch of DM
|
20
|
+
association.class == DataMapper::Associations::ManyToOne::Relationship
|
21
|
+
else
|
22
|
+
# We're using the 0.9 or less branch.
|
23
|
+
association.options[:max].nil?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# This method takes care of converting any associated objects,
|
28
|
+
# in the hash returned by Lathe#assigned_attributes, into their
|
29
|
+
# object ids.
|
30
|
+
#
|
31
|
+
# For example, let's say we have blueprints like this:
|
32
|
+
#
|
33
|
+
# Post.blueprint { }
|
34
|
+
# Comment.blueprint { post }
|
35
|
+
#
|
36
|
+
# Lathe#assigned_attributes will return { :post => ... }, but
|
37
|
+
# we want to pass { :post_id => 1 } to a controller.
|
38
|
+
#
|
39
|
+
# This method takes care of cleaning this up.
|
40
|
+
def self.assigned_attributes_without_associations(lathe)
|
41
|
+
attributes = {}
|
42
|
+
lathe.assigned_attributes.each_pair do |attribute, value|
|
43
|
+
association = lathe.object.class.relationships[attribute]
|
44
|
+
if association && association_is_many_to_one?(association)
|
45
|
+
# DataMapper child_key can have more than one property, but I'm not
|
46
|
+
# sure in what circumstances this would be the case. I'm assuming
|
47
|
+
# here that there's only one property.
|
48
|
+
key = association.child_key.map(&:field).first.to_sym
|
49
|
+
attributes[key] = value.id
|
50
|
+
else
|
51
|
+
attributes[attribute] = value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
attributes
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module DataMapperExtensions
|
59
|
+
def make(*args, &block)
|
60
|
+
lathe = Lathe.run(Machinist::DataMapperAdapter, self.new, *args)
|
61
|
+
unless Machinist.nerfed?
|
62
|
+
lathe.object.save || raise("Save failed")
|
63
|
+
lathe.object.reload
|
64
|
+
end
|
65
|
+
lathe.object(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def make_unsaved(*args)
|
69
|
+
object = Machinist.with_save_nerfed { make(*args) }
|
70
|
+
yield object if block_given?
|
71
|
+
object
|
72
|
+
end
|
73
|
+
|
74
|
+
def plan(*args)
|
75
|
+
lathe = Lathe.run(Machinist::DataMapperAdapter, self.new, *args)
|
76
|
+
Machinist::DataMapperAdapter.assigned_attributes_without_associations(lathe)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
DataMapper::Model.append_extensions(Machinist::Blueprints::ClassMethods)
|
83
|
+
DataMapper::Model.append_extensions(Machinist::DataMapperExtensions)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'machinist/blueprints'
|
3
|
+
|
4
|
+
module Machinist
|
5
|
+
|
6
|
+
module ObjectExtensions
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def make(*args, &block)
|
13
|
+
lathe = Lathe.run(Machinist::ObjectAdapter, self.new, *args)
|
14
|
+
lathe.object(&block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ObjectAdapter
|
20
|
+
def self.has_association?(object, attribute)
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class Object
|
28
|
+
include Machinist::Blueprints
|
29
|
+
include Machinist::ObjectExtensions
|
30
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'machinist/blueprints'
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
module Machinist
|
6
|
+
class SequelAdapter
|
7
|
+
def self.has_association?(object, attribute)
|
8
|
+
object.class.associations.include?(attribute)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.class_for_association(object, attribute)
|
12
|
+
object.class.association_reflection(attribute).associated_class
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.assigned_attributes_without_associations(lathe)
|
16
|
+
attributes = {}
|
17
|
+
lathe.assigned_attributes.each_pair do |attribute, value|
|
18
|
+
association = lathe.object.class.association_reflection(attribute)
|
19
|
+
if association && association[:type] == :many_to_one
|
20
|
+
key = association[:key] || association.default_key
|
21
|
+
attributes[key] = value.send(association.primary_key)
|
22
|
+
else
|
23
|
+
attributes[attribute] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attributes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module SequelExtensions
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def make(*args, &block)
|
37
|
+
lathe = Lathe.run(Machinist::SequelAdapter, self.new, *args)
|
38
|
+
unless Machinist.nerfed?
|
39
|
+
lathe.object.save
|
40
|
+
lathe.object.refresh
|
41
|
+
end
|
42
|
+
lathe.object(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_unsaved(*args)
|
46
|
+
returning(Machinist.with_save_nerfed { make(*args) }) do |object|
|
47
|
+
yield object if block_given?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def plan(*args)
|
52
|
+
lathe = Lathe.run(Machinist::SequelAdapter, self.new, *args)
|
53
|
+
Machinist::SequelAdapter.assigned_attributes_without_associations(lathe)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Sequel::Model
|
60
|
+
include Machinist::Blueprints
|
61
|
+
include Machinist::SequelExtensions
|
62
|
+
end
|
data/lib/sham.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
class Sham
|
2
|
+
@@shams = {}
|
3
|
+
|
4
|
+
# Over-ride module's built-in name method, so we can re-use it for
|
5
|
+
# generating names. This is a bit of a no-no, but we get away with
|
6
|
+
# it in this context.
|
7
|
+
def self.name(*args, &block)
|
8
|
+
if args.empty? && !block_given? && @@shams[:name].nil? # workaround for ZenTest initializer
|
9
|
+
super
|
10
|
+
else
|
11
|
+
method_missing(:name, *args, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.method_missing(symbol, *args, &block)
|
16
|
+
if block_given?
|
17
|
+
@@shams[symbol] = Sham.new(symbol, args.pop || {}, &block)
|
18
|
+
else
|
19
|
+
sham = @@shams[symbol]
|
20
|
+
raise "No sham defined for #{symbol}" if sham.nil?
|
21
|
+
sham.fetch_value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.clear
|
26
|
+
@@shams = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.reset(scope = :before_all)
|
30
|
+
@@shams.values.each { |sham| sham.reset(scope) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.define(&block)
|
34
|
+
Sham.instance_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(name, options = {}, &block)
|
38
|
+
@name = name
|
39
|
+
@generator = block
|
40
|
+
@offset = 0
|
41
|
+
@unique = options.has_key?(:unique) ? options[:unique] : true
|
42
|
+
generate_values(12)
|
43
|
+
end
|
44
|
+
|
45
|
+
def reset(scope)
|
46
|
+
if scope == :before_all
|
47
|
+
@offset, @before_offset = 0, nil
|
48
|
+
elsif @before_offset
|
49
|
+
@offset = @before_offset
|
50
|
+
else
|
51
|
+
@before_offset = @offset
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def fetch_value
|
56
|
+
# Generate more values if we need them.
|
57
|
+
if @offset >= @values.length
|
58
|
+
generate_values(2 * @values.length)
|
59
|
+
raise "Can't generate more unique values for Sham.#{@name}" if @offset >= @values.length
|
60
|
+
end
|
61
|
+
result = @values[@offset]
|
62
|
+
@offset += 1
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def generate_values(count)
|
69
|
+
@values = seeded { (1..count).map(&@generator) }
|
70
|
+
@values.uniq! if @unique
|
71
|
+
end
|
72
|
+
|
73
|
+
def seeded
|
74
|
+
begin
|
75
|
+
srand(1)
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
srand
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/machinist.gemspec
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{machinist}
|
8
|
+
s.version = "1.0.6"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Pete Yandell"]
|
12
|
+
s.date = %q{2010-03-23}
|
13
|
+
s.email = %q{pete@notahat.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.markdown"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
".autotest",
|
19
|
+
".gitignore",
|
20
|
+
"FAQ.markdown",
|
21
|
+
"MIT-LICENSE",
|
22
|
+
"README.markdown",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"init.rb",
|
26
|
+
"lib/machinist.rb",
|
27
|
+
"lib/machinist/active_record.rb",
|
28
|
+
"lib/machinist/blueprints.rb",
|
29
|
+
"lib/machinist/data_mapper.rb",
|
30
|
+
"lib/machinist/object.rb",
|
31
|
+
"lib/machinist/sequel.rb",
|
32
|
+
"lib/sham.rb",
|
33
|
+
"machinist.gemspec",
|
34
|
+
"spec/active_record_spec.rb",
|
35
|
+
"spec/data_mapper_spec.rb",
|
36
|
+
"spec/db/.gitignore",
|
37
|
+
"spec/db/schema.rb",
|
38
|
+
"spec/log/.gitignore",
|
39
|
+
"spec/machinist_spec.rb",
|
40
|
+
"spec/sequel_spec.rb",
|
41
|
+
"spec/sham_spec.rb",
|
42
|
+
"spec/spec_helper.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/notahat/machinist}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.6}
|
48
|
+
s.summary = %q{Fixtures aren't fun. Machinist is.}
|
49
|
+
s.test_files = [
|
50
|
+
"spec/active_record_spec.rb",
|
51
|
+
"spec/data_mapper_spec.rb",
|
52
|
+
"spec/db/schema.rb",
|
53
|
+
"spec/machinist_spec.rb",
|
54
|
+
"spec/sequel_spec.rb",
|
55
|
+
"spec/sham_spec.rb",
|
56
|
+
"spec/spec_helper.rb"
|
57
|
+
]
|
58
|
+
|
59
|
+
if s.respond_to? :specification_version then
|
60
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
+
s.specification_version = 3
|
62
|
+
|
63
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
64
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.8"])
|
65
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<sequel>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<dm-core>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<dm-validations>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<data_objects>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<do_sqlite3>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<rspec>, [">= 1.2.8"])
|
73
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
74
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
75
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
76
|
+
s.add_dependency(%q<dm-validations>, [">= 0"])
|
77
|
+
s.add_dependency(%q<data_objects>, [">= 0"])
|
78
|
+
s.add_dependency(%q<do_sqlite3>, [">= 0"])
|
79
|
+
end
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<rspec>, [">= 1.2.8"])
|
82
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
83
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
84
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
85
|
+
s.add_dependency(%q<dm-validations>, [">= 0"])
|
86
|
+
s.add_dependency(%q<data_objects>, [">= 0"])
|
87
|
+
s.add_dependency(%q<do_sqlite3>, [">= 0"])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|