JamieFlournoy-machinist 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|