blueprints 0.8.2 → 0.9.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/.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)
|