notahat-machinist 0.3.1 → 1.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.
- data/lib/machinist.rb +61 -32
- data/lib/machinist/active_record.rb +49 -67
- data/lib/machinist/blueprints.rb +25 -0
- data/lib/machinist/data_mapper.rb +83 -0
- data/lib/machinist/object.rb +30 -0
- metadata +5 -2
data/lib/machinist.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
require 'active_record'
|
3
1
|
require 'sham'
|
4
|
-
require 'machinist/active_record'
|
5
2
|
|
6
3
|
module Machinist
|
7
4
|
|
@@ -9,63 +6,95 @@ module Machinist
|
|
9
6
|
#
|
10
7
|
# The blueprint is instance_eval'd against the Lathe.
|
11
8
|
class Lathe
|
12
|
-
def self.run(object, *args)
|
9
|
+
def self.run(adapter, object, *args)
|
13
10
|
blueprint = object.class.blueprint
|
14
11
|
named_blueprint = object.class.blueprint(args.shift) if args.first.is_a?(Symbol)
|
15
|
-
attributes
|
12
|
+
attributes = args.pop || {}
|
16
13
|
raise "No blueprint for class #{object.class}" if blueprint.nil?
|
17
|
-
returning self.new(object, attributes) do |lathe|
|
14
|
+
returning self.new(adapter, object, attributes) do |lathe|
|
18
15
|
lathe.instance_eval(&named_blueprint) if named_blueprint
|
19
16
|
lathe.instance_eval(&blueprint)
|
20
17
|
end
|
21
18
|
end
|
22
19
|
|
23
|
-
def initialize(object, attributes = {})
|
24
|
-
@
|
25
|
-
@
|
26
|
-
attributes.each
|
27
|
-
@object.send("#{key}=", value)
|
28
|
-
@assigned_attributes[key.to_sym] = value
|
29
|
-
end
|
20
|
+
def initialize(adapter, object, attributes = {})
|
21
|
+
@adapter = adapter
|
22
|
+
@object = object
|
23
|
+
attributes.each {|key, value| assign_attribute(key, value) }
|
30
24
|
end
|
31
25
|
|
32
|
-
# Undef a couple of methods that are common ActiveRecord attributes.
|
33
|
-
# (Both of these are deprecated in Ruby 1.8 anyway.)
|
34
|
-
undef_method :id if respond_to?(:id)
|
35
|
-
undef_method :type if respond_to?(:type)
|
36
|
-
|
37
26
|
def object
|
38
27
|
yield @object if block_given?
|
39
28
|
@object
|
40
29
|
end
|
41
30
|
|
42
|
-
attr_reader :assigned_attributes
|
43
|
-
|
44
31
|
def method_missing(symbol, *args, &block)
|
45
|
-
if
|
32
|
+
if attribute_assigned?(symbol)
|
33
|
+
# If we've already assigned the attribute, return that.
|
46
34
|
@object.send(symbol)
|
47
|
-
elsif @
|
35
|
+
elsif @adapter.has_association?(@object, symbol) && !@object.send(symbol).nil?
|
36
|
+
# If the attribute is an association and is already assigned, return that.
|
48
37
|
@object.send(symbol)
|
49
38
|
else
|
50
|
-
|
39
|
+
# Otherwise generate a value and assign it.
|
40
|
+
assign_attribute(symbol, generate_attribute_value(symbol, *args, &block))
|
51
41
|
end
|
52
42
|
end
|
43
|
+
|
44
|
+
def assigned_attributes
|
45
|
+
@assigned_attributes ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Undef a couple of methods that are common ActiveRecord attributes.
|
49
|
+
# (Both of these are deprecated in Ruby 1.8 anyway.)
|
50
|
+
undef_method :id if respond_to?(:id)
|
51
|
+
undef_method :type if respond_to?(:type)
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def assign_attribute(key, value)
|
56
|
+
assigned_attributes[key.to_sym] = value
|
57
|
+
@object.send("#{key}=", value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def attribute_assigned?(key)
|
61
|
+
assigned_attributes.has_key?(key.to_sym)
|
62
|
+
end
|
53
63
|
|
54
|
-
def
|
55
|
-
|
64
|
+
def generate_attribute_value(attribute, *args)
|
65
|
+
if block_given?
|
66
|
+
# If we've got a block, use that to generate the value.
|
56
67
|
yield
|
57
|
-
elsif args.empty?
|
58
|
-
|
59
|
-
|
60
|
-
|
68
|
+
elsif !args.empty?
|
69
|
+
# If we've got a constant, just use that.
|
70
|
+
args.first
|
71
|
+
else
|
72
|
+
# Otherwise, look for an association or a sham.
|
73
|
+
if @adapter.has_association?(object, attribute)
|
74
|
+
@adapter.class_for_association(object, attribute).make(args.first || {})
|
61
75
|
else
|
62
76
|
Sham.send(attribute)
|
63
77
|
end
|
64
|
-
else
|
65
|
-
args.first
|
66
78
|
end
|
67
|
-
@assigned_attributes[attribute] = value
|
68
79
|
end
|
69
80
|
|
70
81
|
end
|
82
|
+
|
83
|
+
# This sets a flag that stops make from saving objects, so
|
84
|
+
# that calls to make from within a blueprint don't create
|
85
|
+
# anything inside make_unsaved.
|
86
|
+
def self.with_save_nerfed
|
87
|
+
begin
|
88
|
+
@@nerfed = true
|
89
|
+
yield
|
90
|
+
ensure
|
91
|
+
@@nerfed = false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
@@nerfed = false
|
96
|
+
def self.nerfed?
|
97
|
+
@@nerfed
|
98
|
+
end
|
99
|
+
|
71
100
|
end
|
@@ -1,9 +1,21 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'machinist/blueprints'
|
3
|
+
|
1
4
|
module Machinist
|
2
5
|
|
3
|
-
|
6
|
+
class ActiveRecordAdapter
|
7
|
+
|
8
|
+
def self.has_association?(object, attribute)
|
9
|
+
object.class.reflect_on_association(attribute)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.class_for_association(object, attribute)
|
13
|
+
association = object.class.reflect_on_association(attribute)
|
14
|
+
association && association.klass
|
15
|
+
end
|
4
16
|
|
5
17
|
# This method takes care of converting any associated objects,
|
6
|
-
# in the hash returned by Lathe#
|
18
|
+
# in the hash returned by Lathe#assigned_attributes, into their
|
7
19
|
# object ids.
|
8
20
|
#
|
9
21
|
# For example, let's say we have blueprints like this:
|
@@ -28,89 +40,59 @@ module Machinist
|
|
28
40
|
attributes
|
29
41
|
end
|
30
42
|
|
31
|
-
|
32
|
-
# that calls to make from within a blueprint don't create
|
33
|
-
# anything inside make_unsaved.
|
34
|
-
def self.with_save_nerfed
|
35
|
-
begin
|
36
|
-
@@nerfed = true
|
37
|
-
yield
|
38
|
-
ensure
|
39
|
-
@@nerfed = false
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
@@nerfed = false
|
44
|
-
def self.nerfed?
|
45
|
-
@@nerfed
|
46
|
-
end
|
47
|
-
|
48
|
-
module Extensions
|
49
|
-
def self.included(base)
|
50
|
-
base.extend(ClassMethods)
|
51
|
-
end
|
43
|
+
end
|
52
44
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@blueprints[name] = blueprint if block_given?
|
57
|
-
@blueprints[name]
|
58
|
-
end
|
59
|
-
|
60
|
-
def named_blueprints
|
61
|
-
@blueprints.reject{|name,_| name == :master }.keys
|
62
|
-
end
|
63
|
-
|
64
|
-
def clear_blueprints!
|
65
|
-
@blueprints = {}
|
66
|
-
end
|
67
|
-
|
68
|
-
def make(*args, &block)
|
69
|
-
lathe = Lathe.run(self.new, *args)
|
70
|
-
unless Machinist::ActiveRecord.nerfed?
|
71
|
-
lathe.object.save!
|
72
|
-
lathe.object.reload
|
73
|
-
end
|
74
|
-
lathe.object(&block)
|
75
|
-
end
|
76
|
-
|
77
|
-
def make_unsaved(*args)
|
78
|
-
returning(Machinist::ActiveRecord.with_save_nerfed { make(*args) }) do |object|
|
79
|
-
yield object if block_given?
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def plan(*args)
|
84
|
-
lathe = Lathe.run(self.new, *args)
|
85
|
-
Machinist::ActiveRecord.assigned_attributes_without_associations(lathe)
|
86
|
-
end
|
87
|
-
end
|
45
|
+
module ActiveRecordExtensions
|
46
|
+
def self.included(base)
|
47
|
+
base.extend(ClassMethods)
|
88
48
|
end
|
89
49
|
|
90
|
-
module
|
50
|
+
module ClassMethods
|
91
51
|
def make(*args, &block)
|
92
|
-
lathe = Lathe.run(self.
|
93
|
-
unless Machinist
|
52
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.new, *args)
|
53
|
+
unless Machinist.nerfed?
|
94
54
|
lathe.object.save!
|
95
55
|
lathe.object.reload
|
96
56
|
end
|
97
57
|
lathe.object(&block)
|
98
58
|
end
|
99
59
|
|
60
|
+
def make_unsaved(*args)
|
61
|
+
returning(Machinist.with_save_nerfed { make(*args) }) do |object|
|
62
|
+
yield object if block_given?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
100
66
|
def plan(*args)
|
101
|
-
lathe = Lathe.run(self.
|
102
|
-
Machinist::
|
67
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.new, *args)
|
68
|
+
Machinist::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
103
69
|
end
|
104
70
|
end
|
71
|
+
end
|
105
72
|
|
73
|
+
module ActiveRecordHasManyExtensions
|
74
|
+
def make(*args, &block)
|
75
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.build, *args)
|
76
|
+
unless Machinist.nerfed?
|
77
|
+
lathe.object.save!
|
78
|
+
lathe.object.reload
|
79
|
+
end
|
80
|
+
lathe.object(&block)
|
81
|
+
end
|
82
|
+
|
83
|
+
def plan(*args)
|
84
|
+
lathe = Lathe.run(Machinist::ActiveRecordAdapter, self.build, *args)
|
85
|
+
Machinist::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
86
|
+
end
|
106
87
|
end
|
107
|
-
end
|
108
88
|
|
89
|
+
end
|
109
90
|
|
110
91
|
class ActiveRecord::Base
|
111
|
-
include Machinist::
|
92
|
+
include Machinist::Blueprints
|
93
|
+
include Machinist::ActiveRecordExtensions
|
112
94
|
end
|
113
95
|
|
114
|
-
class ActiveRecord::Associations::
|
115
|
-
include Machinist::
|
96
|
+
class ActiveRecord::Associations::HasManyAssociation
|
97
|
+
include Machinist::ActiveRecordHasManyExtensions
|
116
98
|
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
|
+
returning(Machinist.with_save_nerfed { make(*args) }) do |object|
|
70
|
+
yield object if block_given?
|
71
|
+
end
|
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
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: notahat-machinist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Yandell
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-16 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -24,7 +24,10 @@ extra_rdoc_files: []
|
|
24
24
|
files:
|
25
25
|
- lib/machinist.rb
|
26
26
|
- lib/sham.rb
|
27
|
+
- lib/machinist/blueprints.rb
|
28
|
+
- lib/machinist/object.rb
|
27
29
|
- lib/machinist/active_record.rb
|
30
|
+
- lib/machinist/data_mapper.rb
|
28
31
|
has_rdoc: false
|
29
32
|
homepage: http://github.com/notahat/machinist
|
30
33
|
post_install_message:
|