blueprints 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +82 -18
- data/LICENSE +1 -1
- data/README.rdoc +38 -8
- data/Rakefile +11 -35
- data/blueprints.gemspec +29 -123
- data/features/support/env.rb +7 -10
- data/lib/blueprints.rb +68 -12
- data/lib/blueprints/blueprint.rb +39 -19
- data/lib/blueprints/buildable.rb +92 -74
- data/lib/blueprints/configuration.rb +18 -4
- data/lib/blueprints/context.rb +148 -5
- data/lib/blueprints/database_cleaner_fix.rb +9 -0
- data/lib/blueprints/dependency.rb +32 -21
- data/lib/blueprints/eval_context.rb +51 -0
- data/lib/blueprints/extensions.rb +115 -0
- data/lib/blueprints/extensions/rspec.rb +3 -1
- data/lib/blueprints/helper.rb +52 -20
- data/lib/blueprints/namespace.rb +31 -12
- data/lib/blueprints/root_namespace.rb +24 -25
- data/lib/blueprints/version.rb +3 -0
- data/spec/{active_record/blueprint.rb → blueprint.rb} +14 -17
- data/spec/{active_record/blueprints_spec.rb → blueprints_spec.rb} +40 -59
- data/spec/spec_helper.rb +34 -0
- data/spec/support/active_record/database.yml.example +7 -0
- data/spec/support/active_record/initializer.rb +15 -0
- data/spec/{active_record/fixtures → support/active_record}/schema.rb +0 -0
- data/spec/support/dm-core/initializer.rb +31 -0
- data/spec/support/mongo_mapper/database.yml.example +2 -0
- data/spec/support/mongo_mapper/initializer.rb +20 -0
- data/spec/support/mongoid/database.yml.example +2 -0
- data/spec/support/mongoid/initializer.rb +23 -0
- data/spec/support/none/initializer.rb +63 -0
- data/spec/unit/active_record_spec.rb +1 -6
- data/spec/unit/blueprint_spec.rb +91 -20
- data/spec/unit/blueprints_spec.rb +44 -0
- data/spec/unit/buildable_spec.rb +37 -6
- data/spec/unit/configuration_spec.rb +11 -0
- data/spec/unit/context_spec.rb +100 -0
- data/spec/unit/dependency_spec.rb +24 -19
- data/spec/unit/eval_context_spec.rb +56 -0
- data/spec/unit/fixtures.rb +61 -0
- data/spec/unit/namespace_spec.rb +59 -11
- data/spec/unit/spec_helper.rb +8 -16
- data/test/blueprints_test.rb +40 -59
- data/test/test_helper.rb +6 -16
- data/test_all.sh +45 -0
- metadata +178 -61
- data/VERSION +0 -1
- data/lib/blueprints/core_ext.rb +0 -69
- data/lib/blueprints/file_context.rb +0 -37
- data/spec/active_record/fixtures/database.yml.example +0 -8
- data/spec/active_record/fixtures/fruit.rb +0 -3
- data/spec/active_record/fixtures/tree.rb +0 -4
- data/spec/active_record/spec_helper.rb +0 -37
- data/spec/no_db/blueprint.rb +0 -9
- data/spec/no_db/blueprints_spec.rb +0 -45
- data/spec/no_db/fixtures/fruit.rb +0 -15
- data/spec/no_db/spec_helper.rb +0 -14
- data/spec/test_all.sh +0 -39
@@ -1,16 +1,26 @@
|
|
1
1
|
module Blueprints
|
2
|
+
# Contains configuration of blueprints. Instance of this is yielded in Blueprints.enable block.
|
3
|
+
# @example Configuring through Blueprints.enable block
|
4
|
+
# Blueprints.enable do |config|
|
5
|
+
# config.prebuild = :user, :profile
|
6
|
+
# end
|
7
|
+
# @example Configuring directly
|
8
|
+
# Blueprints.config.transactions = false
|
2
9
|
class Configuration
|
3
|
-
SUPPORTED_ORMS = [nil, :active_record]
|
4
10
|
# Allows passing custom filename pattern in case blueprints are held in place other than spec/blueprint, test/blueprint, blueprint.
|
5
11
|
attr_reader :filename
|
6
12
|
# Allows passing scenarios that should be prebuilt and available in all tests. Works similarly to fixtures.
|
7
13
|
attr_accessor :prebuild
|
8
|
-
# Allows passing custom root folder to use in case of non rails project. Defaults to
|
14
|
+
# Allows passing custom root folder to use in case of non rails project. Defaults to Rails.root or current folder if Rails is not defined.
|
9
15
|
attr_reader :root
|
10
16
|
# By default blueprints runs each test in it's own transaction. This may sometimes be not desirable so this options allows to turn this off.
|
11
17
|
attr_accessor :transactions
|
18
|
+
# Default attributes are used when blueprints has no name specified.
|
19
|
+
attr_reader :default_attributes
|
12
20
|
|
13
|
-
#
|
21
|
+
# Initializes new Configuration object with default attributes.
|
22
|
+
# By defaults filename patterns are: blueprint.rb and blueprint/*.rb in spec, test and root directories.
|
23
|
+
# Also by default prebuildable blueprints list is empty, transactions are enabled and root is set to Rails.root or current directory.
|
14
24
|
def initialize
|
15
25
|
self.filename = [nil, "spec", "test"].map do |dir|
|
16
26
|
["blueprint"].map do |file|
|
@@ -18,10 +28,10 @@ module Blueprints
|
|
18
28
|
["#{path}.rb", File.join(path, "*.rb")]
|
19
29
|
end
|
20
30
|
end.flatten
|
21
|
-
@orm = :active_record
|
22
31
|
@prebuild = []
|
23
32
|
@transactions = true
|
24
33
|
@root = defined?(Rails) ? Rails.root : Pathname.pwd
|
34
|
+
@default_attributes = [:name]
|
25
35
|
end
|
26
36
|
|
27
37
|
def filename=(value)
|
@@ -31,5 +41,9 @@ module Blueprints
|
|
31
41
|
def root=(value)
|
32
42
|
@root = Pathname.new(value)
|
33
43
|
end
|
44
|
+
|
45
|
+
def default_attributes=(value)
|
46
|
+
@default_attributes = Array(value)
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
data/lib/blueprints/context.rb
CHANGED
@@ -1,11 +1,154 @@
|
|
1
1
|
module Blueprints
|
2
|
-
# Class that blueprint
|
2
|
+
# Class that blueprint files are evaluated against. Has methods for setting and retrieving attributes and dependencies.
|
3
|
+
# Allows defining new blueprints and namespaces.
|
3
4
|
class Context
|
4
|
-
|
5
|
+
@@chain = []
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
attr_reader :dependencies, :file
|
8
|
+
|
9
|
+
# Initializes new context with passed parent, attributes, dependencies, file and namespace.
|
10
|
+
# Attributes and dependencies are automatically merged with parents' attributes and dependencies.
|
11
|
+
# File and namespace are automatically set to parent counterparts unless they are explicitly changed.
|
12
|
+
# @param [Hash] options Options for new context.
|
13
|
+
# @option options [Hash] :attributes ({}) List of attributes, merged with parent attributes.
|
14
|
+
# @option options [Array<String, Symbol>] :dependencies ([]) List of dependencies, merged with parent dependencies.
|
15
|
+
# @option options [Pathname] :file File this context is evaluated in. Should be passed for top level contexts only.
|
16
|
+
# @option options [Blueprints::Namespace] :namespace Namespace that new blueprints and namespaces should be children of.
|
17
|
+
# @option options [Blueprints::Context] :parent Parent context that is used to retrieve unchanged values.
|
18
|
+
def initialize(options = {})
|
19
|
+
options.assert_valid_keys(:dependencies, :attributes, :file, :parent, :namespace)
|
20
|
+
@dependencies = (options[:dependencies] || []).collect(&:to_sym)
|
21
|
+
@attributes = options[:attributes] || {}
|
22
|
+
@file = options[:file]
|
23
|
+
@namespace = options[:namespace]
|
24
|
+
|
25
|
+
if parent = options[:parent]
|
26
|
+
@attributes.reverse_merge!(parent.attributes)
|
27
|
+
@dependencies = (parent.dependencies + @dependencies).uniq
|
28
|
+
@file ||= parent.file
|
29
|
+
@namespace ||= parent.namespace
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Checks if two contexts are equal by comparing attributes, dependencies, namespace and file
|
34
|
+
# @param [Blueprints::Context] context Context to compare this one to.
|
35
|
+
# @return [true, false] Whether contexts are equal or not.
|
36
|
+
def ==(context)
|
37
|
+
@dependencies == context.dependencies and @attributes == context.attributes and @file == context.file and @namespace == context.namespace
|
38
|
+
end
|
39
|
+
|
40
|
+
# Defines a new blueprint by name and block passed.
|
41
|
+
# @example Define blueprint.
|
42
|
+
# blueprint :user do
|
43
|
+
# User.blueprint :name => 'User'
|
44
|
+
# end
|
45
|
+
# @param name (see Buildable#initialize)
|
46
|
+
# @return [Blueprints::Blueprint] Newly defined blueprint.
|
47
|
+
def blueprint(name = nil, &block)
|
48
|
+
Blueprint.new(name, self, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @overload namespace(name, &block)
|
52
|
+
# Defines new namespace by name, and evaluates block against it.
|
53
|
+
# @example Define namespace and blueprint in it.
|
54
|
+
# namespace :banned do
|
55
|
+
# blueprint :user do
|
56
|
+
# User.blueprint :name => 'User'
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# @param [String, Symbol] name Name of namespace.
|
60
|
+
# @return [Blueprints::Namespace] Newly defined namespace.
|
61
|
+
# @overload namespace
|
62
|
+
# Returns namespace for this context.
|
63
|
+
# @return [Blueprints::Namespace] Namespace for this context.
|
64
|
+
def namespace(name = nil, &block)
|
65
|
+
if name
|
66
|
+
Namespace.new(name, self).tap do |namespace|
|
67
|
+
with_context(:namespace => namespace, &block)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
@namespace
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @overload attributes(new_attributes, &block)
|
75
|
+
# Yields and returns child context that has new attributes set.
|
76
|
+
# @example Define blueprint with attributes
|
77
|
+
# attributes(:name => 'User').blueprint(:user) do
|
78
|
+
# User.blueprint attributes
|
79
|
+
# end
|
80
|
+
# @example Define multiple blueprints with same attributes
|
81
|
+
# attributes(:name => 'User') do
|
82
|
+
# blueprint(:user1) do
|
83
|
+
# User.blueprint attributes
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# blueprint(:user2) do
|
87
|
+
# User.blueprint attributes
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
# @param [Hash] new_attributes Attributes for child context.
|
91
|
+
# @return [Blueprints::Context] Child context
|
92
|
+
# @overload attributes
|
93
|
+
# Returns attributes of context.
|
94
|
+
# @return [Hash] Attributes of context.
|
95
|
+
def attributes(new_attributes = nil, &block)
|
96
|
+
if new_attributes
|
97
|
+
with_context(:attributes => new_attributes, &block)
|
98
|
+
else
|
99
|
+
@attributes
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Yields and returns child context that has dependencies set.
|
104
|
+
# @example Define blueprint with dependencies
|
105
|
+
# depends_on(:user, :admin).blueprint(:user_and_admin)
|
106
|
+
# @example Define multiple blueprints with same dependencies.
|
107
|
+
# depends_on :user, :admin do
|
108
|
+
# blueprint :user_and_admin
|
109
|
+
# blueprint :admin_and_user
|
110
|
+
# end
|
111
|
+
# @param [Array<Symbol, String>] new_dependencies Dependencies for child context.
|
112
|
+
# @return [Blueprints::Context] Child context
|
113
|
+
def depends_on(*new_dependencies, &block)
|
114
|
+
with_context(:dependencies => new_dependencies, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Yields and returns child context that has new options set.
|
118
|
+
# @param options (see Context#initialize)
|
119
|
+
# @return [Blueprints::Context] Child context
|
120
|
+
def with_context(options, &block)
|
121
|
+
Context.eval_within_context(options.merge(:parent => self), &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Initializes new Blueprints::Dependency object.
|
125
|
+
# @overload d(name, options = {})
|
126
|
+
# @param name (see Dependency#initialize)
|
127
|
+
# @param options (see Dependency#initialize)
|
128
|
+
# @overload d(name, instance_variable_name, options = {})
|
129
|
+
# @param name (see Dependency#initialize)
|
130
|
+
# @param instance_variable_name (see Dependency#initialize)
|
131
|
+
# @param options (see Dependency#initialize)
|
132
|
+
def d(*args)
|
133
|
+
Dependency.new(*args)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Return current context.
|
137
|
+
# @return [Blueprints::Context] Current context.
|
138
|
+
def self.current
|
139
|
+
@@chain.last
|
140
|
+
end
|
141
|
+
|
142
|
+
# Creates child context and sets it as current. Evaluates block and file within child context if any are passed.
|
143
|
+
# @param [Hash] new_options Options for child context.
|
144
|
+
def self.eval_within_context(new_options, &block)
|
145
|
+
@@chain << context = new(new_options)
|
146
|
+
|
147
|
+
file = new_options[:file]
|
148
|
+
context.instance_eval(File.read(file), file) if file
|
149
|
+
context.instance_eval(&block) if block
|
150
|
+
|
151
|
+
@@chain.pop
|
9
152
|
end
|
10
153
|
end
|
11
154
|
end
|
@@ -1,35 +1,46 @@
|
|
1
|
-
# Class for defining blueprint dependencies.
|
2
|
-
# * name - pass the name of blueprint to build when trying to access value of this dependency.
|
3
|
-
# * iv_name (optional) - pass the name of instance variable to use for value. Defaults to same name as blueprint name.
|
4
|
-
# * options (optional) - pass options that are then passed to blueprint when building.
|
5
|
-
# Examples:
|
6
|
-
# Blueprints::Dependency.new(:blueprint).value # Builds blueprint 'blueprint' and returns value of @blueprint instance variable
|
7
|
-
# Blueprints::Dependency.new(:blueprint, value).value # Builds blueprint 'blueprint' and returns value of @value instance variable
|
8
|
-
# Blueprints::Dependency.new(:blueprint, :option => true).value # Builds blueprint 'blueprint' with options and returns value of @value instance variable
|
9
|
-
#
|
10
|
-
# Blueprints::Dependency objects also catch all missing methods. They are later replayed on instance variable when getting value. Example:
|
11
|
-
# d = Blueprints::Dependency.new(:blueprint).name.size
|
12
|
-
# d.value # => 4 when @blueprint.name == 'John'
|
1
|
+
# Class for defining blueprint dependencies.
|
13
2
|
class Blueprints::Dependency
|
14
3
|
instance_methods.each { |m| undef_method m if m =~ /^(to_|id$)/ }
|
15
4
|
|
16
|
-
# Initializes new
|
5
|
+
# Initializes new Blueprints::Dependency object.
|
6
|
+
# @example Build blueprint 'blueprint' and returns value of @blueprint instance variable.
|
7
|
+
# Blueprints::Dependency.new(:blueprint)
|
8
|
+
# @example Build blueprint 'blueprint' and returns value of @value instance variable.
|
9
|
+
# Blueprints::Dependency.new(:blueprint, value)
|
10
|
+
# @example Build blueprint 'blueprint' with options and returns value of @value instance variable.
|
11
|
+
# Blueprints::Dependency.new(:blueprint, :option => true)
|
12
|
+
# @example Register called methods
|
13
|
+
# d = Blueprints::Dependency.new(:blueprint).name.size
|
14
|
+
# @overload d(name, options = {})
|
15
|
+
# Use result of blueprint/namespace +name+ and pass options when building.
|
16
|
+
# @param [Symbol, String] name Name of blueprint/namespace.
|
17
|
+
# @param [Hash] options Options to pass when building blueprint/namespace.
|
18
|
+
# @overload d(name, instance_variable_name, options = {})
|
19
|
+
# Build blueprint/namespace with options and use differently names instance variable as result.
|
20
|
+
# @param [Symbol, String] name Name of blueprint/namespace.
|
21
|
+
# @param [Symbol, String] instance_variable_name Name of instance variable to use as a result.
|
22
|
+
# @param [Hash] options Options to pass when building blueprint/namespace.
|
17
23
|
def initialize(name, *args)
|
18
|
-
@name
|
19
|
-
@options
|
20
|
-
@iv_name
|
24
|
+
@name = name
|
25
|
+
@options = args.extract_options!
|
26
|
+
@iv_name = (args.first || @name).to_s.gsub('.', '_')
|
21
27
|
@registry = []
|
22
28
|
end
|
23
29
|
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
# Returns block that builds blueprint (if necessary) takes instance variable for this dependency and calls all methods from method registry.
|
31
|
+
# @return [Proc] Proc that can be called to return value for this dependency.
|
32
|
+
def to_proc
|
33
|
+
name, options, registry, variable_name = @name, @options, @registry, @iv_name
|
34
|
+
Proc.new do
|
35
|
+
build name => options
|
36
|
+
registry.inject(instance_variable_get(:"@#{variable_name}")) do |value, (method, args, block)|
|
37
|
+
value.send(method, *args, &block)
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
42
|
# Catches all missing methods to later replay when asking for value.
|
43
|
+
# @return [Blueprints::Dependency] self
|
33
44
|
def method_missing(method, *args, &block)
|
34
45
|
@registry << [method, args, block]
|
35
46
|
self
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Blueprints
|
2
|
+
class EvalContext
|
3
|
+
# Copy instance variables to another object.
|
4
|
+
# @param target Object to copy instance variables to.
|
5
|
+
def copy_instance_variables(target)
|
6
|
+
instance_variables.each do |iv_name|
|
7
|
+
target.instance_variable_set(iv_name, instance_variable_get(iv_name))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets options and attributes and evaluates block against self.
|
12
|
+
# @param [Blueprints::Context] context Context of buildable object. Used to extract attributes.
|
13
|
+
# @param [Hash] options Options hash, merged into attributes.
|
14
|
+
def instance_eval(context, options, &block)
|
15
|
+
options = normalize_hash(options)
|
16
|
+
define_singleton_method(:options) { options }
|
17
|
+
attributes = normalize_hash(context.attributes).merge(options)
|
18
|
+
define_singleton_method(:attributes) { attributes }
|
19
|
+
|
20
|
+
super(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Builds blueprints by delegating to root namespace.
|
24
|
+
# @param [Array<String, Symbol>] blueprints Names of buildables.
|
25
|
+
# @return Result of last buildable.
|
26
|
+
def build(*blueprints)
|
27
|
+
Namespace.root.build(blueprints)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Normalizes attributes hash by evaluating all Proc and Blueprints::Dependency objects against itself.
|
31
|
+
# @param [Hash] hash Attributes hash.
|
32
|
+
# @return [Hash] Normalized hash.
|
33
|
+
def normalize_hash(hash)
|
34
|
+
hash.each_with_object({}) do |(attr, value), normalized|
|
35
|
+
normalized[attr] = if value.respond_to?(:to_proc) and not Symbol === value
|
36
|
+
instance_exec(&value)
|
37
|
+
else
|
38
|
+
value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def define_singleton_method(name, &block)
|
46
|
+
singleton_class.class_eval do
|
47
|
+
define_method(name, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Blueprints::Extensions
|
2
|
+
# Here to support ActiveSupport 2.x. Will be removed once support for ActiveRecord 2.3 is terminated.
|
3
|
+
module Extendable
|
4
|
+
def self.included(mod)
|
5
|
+
if defined?(ActiveSupport::Concern)
|
6
|
+
mod.extend ActiveSupport::Concern
|
7
|
+
else
|
8
|
+
def mod.included(mod)
|
9
|
+
mod.extend Blueprints::Extensions::Blueprintable::ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Include this module into your class if you need klass.blueprint and object.blueprint methods.
|
16
|
+
module Blueprintable
|
17
|
+
include Extendable
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# @overload blueprint(attributes)
|
21
|
+
# Does same as +create!+ method except that it also bypasses attr_protected and attr_accessible. Typically used in blueprint block.
|
22
|
+
# @example Create post for user
|
23
|
+
# @user.posts.blueprint(:title => 'first post', :text => 'My first post')
|
24
|
+
# @param [Hash, Array<Hash>] attributes Attributes used to create objects.
|
25
|
+
# @return Created object(s).
|
26
|
+
# @overload blueprint(name, attributes = {})
|
27
|
+
# Defines new blueprint that creates an object with attributes passed.
|
28
|
+
# @example Create blueprint named :post.
|
29
|
+
# Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user)).depends_on(:board)
|
30
|
+
# @param [String, Symbol, Hash] name Name of blueprint.
|
31
|
+
# @param [Hash] attributes Attributes hash.
|
32
|
+
# @return [Blueprints::Blueprint] Defined blueprint.
|
33
|
+
def blueprint(*args)
|
34
|
+
if Blueprints::Context.current
|
35
|
+
attrs = args.extract_options!
|
36
|
+
define_blueprint(args.first, attrs)
|
37
|
+
else
|
38
|
+
objects = args.collect { |attrs| blueprint_object(attrs) }
|
39
|
+
args.size == 1 ? objects.first : objects
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def define_blueprint(name, attrs)
|
46
|
+
klass = self
|
47
|
+
Blueprints::Context.current.attributes(attrs).blueprint(name) { klass.blueprint attributes }
|
48
|
+
end
|
49
|
+
|
50
|
+
def blueprint_object(attrs)
|
51
|
+
new.tap { |object| object.blueprint(attrs) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Updates attributes of object by calling setter methods. Does same as +update_attributes!+ except it also bypasses attr_protected and attr_accessible.
|
56
|
+
# @param [Hash] attributes Attributes that are used to update object.
|
57
|
+
def blueprint(attributes)
|
58
|
+
attributes.each do |attribute, value|
|
59
|
+
blueprint_attribute attribute, value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def blueprint_attribute(attribute, value)
|
66
|
+
send(:"#{attribute}=", value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Include this instead of Blueprints::Extensions::Blueprintable if record needs to persist, includes Blueprints::Extensions::Blueprintable
|
71
|
+
module Saveable
|
72
|
+
include Extendable
|
73
|
+
include Blueprintable
|
74
|
+
|
75
|
+
# Overrides object.blueprint to also call save!
|
76
|
+
def blueprint(attributes)
|
77
|
+
super(attributes)
|
78
|
+
save!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Include this instead of Blueprints::Extensions::Saveable if you want non bang save method (eg.using datamapper)
|
83
|
+
module SoftSaveable
|
84
|
+
include Extendable
|
85
|
+
include Blueprintable
|
86
|
+
|
87
|
+
# Overrides object.blueprint to also call save
|
88
|
+
def blueprint(attributes)
|
89
|
+
super(attributes)
|
90
|
+
save
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Include this instead of Blueprints::Extensions::Saveable if you need support for dynamic attributes (eg. using mongodb)
|
95
|
+
module DynamicSaveable
|
96
|
+
include Extendable
|
97
|
+
include Saveable
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def blueprint_attribute(attribute, value)
|
102
|
+
setter = :"#{attribute}="
|
103
|
+
if respond_to?(setter)
|
104
|
+
send(setter, value)
|
105
|
+
else
|
106
|
+
write_attribute(attribute, value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
ActiveRecord::Base.send(:include, Blueprints::Extensions::Saveable) if defined?(ActiveRecord)
|
113
|
+
Mongoid::Document.send(:include, Blueprints::Extensions::DynamicSaveable) if defined?(Mongoid)
|
114
|
+
MongoMapper::Document.send(:append_inclusions, Blueprints::Extensions::DynamicSaveable) if defined?(MongoMapper)
|
115
|
+
DataMapper::Model.send(:append_inclusions, Blueprints::Extensions::SoftSaveable) if defined?(DataMapper)
|