notahat-machinist 0.3.1 → 1.0.0

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