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 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 = args.pop || {}
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
- @object = object
25
- @assigned_attributes = {}
26
- attributes.each do |key, value|
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 @assigned_attributes.has_key?(symbol)
32
+ if attribute_assigned?(symbol)
33
+ # If we've already assigned the attribute, return that.
46
34
  @object.send(symbol)
47
- elsif @object.class.reflect_on_association(symbol) && !@object.send(symbol).nil?
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
- @object.send("#{symbol}=", generate_attribute(symbol, args, &block))
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 generate_attribute(attribute, args)
55
- value = if block_given?
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
- association = @object.class.reflect_on_association(attribute)
59
- if association
60
- association.class_name.constantize.make(args.first || {})
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
- module ActiveRecord
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#assigned_attributed, into their
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
- # This sets a flag that stops make from saving objects, so
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
- module ClassMethods
54
- def blueprint(name = :master, &blueprint)
55
- @blueprints ||= {}
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 BelongsToExtensions
50
+ module ClassMethods
91
51
  def make(*args, &block)
92
- lathe = Lathe.run(self.build, *args)
93
- unless Machinist::ActiveRecord.nerfed?
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.build, *args)
102
- Machinist::ActiveRecord.assigned_attributes_without_associations(lathe)
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::ActiveRecord::Extensions
92
+ include Machinist::Blueprints
93
+ include Machinist::ActiveRecordExtensions
112
94
  end
113
95
 
114
- class ActiveRecord::Associations::BelongsToAssociation
115
- include Machinist::ActiveRecord::BelongsToExtensions
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.3.1
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-02-25 00:00:00 -08:00
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: