blueprints_boy 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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.travis.yml +20 -0
- data/Appraisals +37 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +26 -0
- data/Rakefile +76 -0
- data/blueprints_boy.gemspec +27 -0
- data/gemfiles/ar3.1.gemfile +8 -0
- data/gemfiles/ar3.2.gemfile +8 -0
- data/gemfiles/ar4.0.gemfile +8 -0
- data/gemfiles/ar4.1.gemfile +8 -0
- data/gemfiles/mongoid2.gemfile +8 -0
- data/gemfiles/mongoid3.gemfile +8 -0
- data/gemfiles/mongoid4.gemfile +8 -0
- data/gemfiles/noorm.gemfile +5 -0
- data/integration/active_record/active_record_spec.rb +38 -0
- data/integration/active_record/active_record_truncation_spec.rb +26 -0
- data/integration/active_record/blueprints.rb +1 -0
- data/integration/active_record/setup.rb +8 -0
- data/integration/cucumber/blueprints.feature +20 -0
- data/integration/cucumber/step_definitions/blueprints_steps.rb +17 -0
- data/integration/cucumber/support/env.rb +11 -0
- data/integration/minitest/blueprints.rb +11 -0
- data/integration/minitest/test_minispec.rb +41 -0
- data/integration/minitest/test_minitest.rb +28 -0
- data/integration/mongoid/blueprints.rb +1 -0
- data/integration/mongoid/mongoid_spec.rb +50 -0
- data/integration/rspec/blueprints.rb +13 -0
- data/integration/rspec/rspec_spec.rb +113 -0
- data/integration/shared.rb +9 -0
- data/lib/blueprints_boy/blueprint.rb +59 -0
- data/lib/blueprints_boy/configuration.rb +48 -0
- data/lib/blueprints_boy/context.rb +53 -0
- data/lib/blueprints_boy/dependency.rb +23 -0
- data/lib/blueprints_boy/errors.rb +10 -0
- data/lib/blueprints_boy/factories.rb +18 -0
- data/lib/blueprints_boy/helper.rb +47 -0
- data/lib/blueprints_boy/integration/active_record.rb +11 -0
- data/lib/blueprints_boy/integration/cucumber.rb +9 -0
- data/lib/blueprints_boy/integration/minitest.rb +23 -0
- data/lib/blueprints_boy/integration/mongoid.rb +11 -0
- data/lib/blueprints_boy/integration/rspec.rb +20 -0
- data/lib/blueprints_boy/manager.rb +87 -0
- data/lib/blueprints_boy/railtie.rb +24 -0
- data/lib/blueprints_boy/registry.rb +28 -0
- data/lib/blueprints_boy/version.rb +3 -0
- data/lib/blueprints_boy.rb +62 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/empty_file.rb +0 -0
- data/spec/support/fixtures.rb +41 -0
- data/spec/support/manager_fixture.rb +3 -0
- data/spec/unit/blueprint_spec.rb +102 -0
- data/spec/unit/blueprints_boy_spec.rb +15 -0
- data/spec/unit/configuration_spec.rb +41 -0
- data/spec/unit/context_spec.rb +145 -0
- data/spec/unit/dependency_spec.rb +56 -0
- data/spec/unit/factories_spec.rb +27 -0
- data/spec/unit/manager_spec.rb +122 -0
- data/spec/unit/registry_spec.rb +47 -0
- metadata +189 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rspec'
|
3
|
+
require 'blueprints_boy'
|
4
|
+
require_relative '../shared'
|
5
|
+
|
6
|
+
BlueprintsBoy.enable do |config|
|
7
|
+
config.root = File.dirname(__FILE__)
|
8
|
+
config.global = :global_cherry
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'rspec integration' do
|
12
|
+
describe "set" do
|
13
|
+
it "should allow setting methods directly" do
|
14
|
+
set :apple, 'apple'
|
15
|
+
apple.should == 'apple'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return value that is set" do
|
19
|
+
set(:apple, 'apple').should == 'apple'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should auto set variable" do
|
23
|
+
autoset :apple, 'apple'
|
24
|
+
apple.should == 'apple'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not auto set variable if it's already set" do
|
28
|
+
set :variable, :correct
|
29
|
+
autoset :variable, :incorrect
|
30
|
+
variable.should == :correct
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should still autoset variable in blueprint_data even if environment defines method with same name" do
|
34
|
+
def self.variable
|
35
|
+
end
|
36
|
+
|
37
|
+
autoset :variable, :correct
|
38
|
+
variable.should be_nil
|
39
|
+
blueprint_data(:variable).should == :correct
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should still return new value if variable is already set" do
|
43
|
+
set :variable, :correct
|
44
|
+
autoset(:variable, :incorrect).should == :incorrect
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should allow reaching fixtures through fixtures method" do
|
48
|
+
set :apple, 'apple'
|
49
|
+
blueprint_data(:apple).should == 'apple'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "build" do
|
54
|
+
it "should allow building blueprint" do
|
55
|
+
build :apple
|
56
|
+
apple.should == 'apple'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should allow building several blueprints" do
|
60
|
+
build :apple, :orange
|
61
|
+
apple.should == 'apple'
|
62
|
+
orange.should == 'orange'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not allow to reach blueprints from previous specs" do
|
66
|
+
blueprint_data(:apple).should be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "build with" do
|
71
|
+
it "should allow building using different strategy" do
|
72
|
+
build_with :attributes, :orange
|
73
|
+
orange.should == {name: 'orange'}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "build!" do
|
78
|
+
it "allows building blueprint twice" do
|
79
|
+
orange1 = build! :orange
|
80
|
+
orange2 = build! :orange => {name: 'not apple'}
|
81
|
+
orange1.should == 'orange'
|
82
|
+
orange2.should == 'not apple'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "build_new" do
|
87
|
+
it "builds blueprint using :new strategy" do
|
88
|
+
build_new :orange
|
89
|
+
orange.should == 'new orange'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "build_attributes" do
|
94
|
+
it "returns attributes for building" do
|
95
|
+
build_attributes :orange => {size: 2}
|
96
|
+
orange.should == {name: 'orange', size: 2}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "class level set" do
|
101
|
+
build :apple
|
102
|
+
|
103
|
+
it "should build blueprints in before filter" do
|
104
|
+
apple.should == 'apple'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "global" do
|
109
|
+
it "should build global blueprints" do
|
110
|
+
global_cherry.should == 'cherry'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class BlueprintsBoy::Blueprint
|
2
|
+
Data = Struct.new(:name, :options, :attributes, :factory)
|
3
|
+
attr_reader :name, :context
|
4
|
+
|
5
|
+
def initialize(context, name, attrs = {}, &block)
|
6
|
+
@context = context
|
7
|
+
@name = name.to_sym
|
8
|
+
@strategies = {
|
9
|
+
create: block,
|
10
|
+
attributes: proc { |data| data.attributes }
|
11
|
+
}
|
12
|
+
attributes(attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(environment, strategy, options = {})
|
16
|
+
data = Data.new(@name, options, normalized_attributes(environment).merge(options), @context.factory_class)
|
17
|
+
block = @strategies[strategy]
|
18
|
+
block ||= BlueprintsBoy.factories[@context.factory_class, strategy] if @context.factory_class
|
19
|
+
if block
|
20
|
+
environment.autoset(@name, environment.instance_exec(data, &block))
|
21
|
+
else
|
22
|
+
raise BlueprintsBoy::StrategyNotFound, "Blueprint #{@name.inspect} does not define strategy #{strategy.inspect}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def depends_on(*dependencies)
|
27
|
+
update_context dependencies, nil, nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def attributes(attributes)
|
31
|
+
update_context nil, attributes, nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def factory(factory_class)
|
35
|
+
update_context nil, nil, factory_class
|
36
|
+
end
|
37
|
+
|
38
|
+
def blueprint(strategy, &block)
|
39
|
+
@strategies[strategy] = block
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def normalized_attributes(environment)
|
45
|
+
@context.attrs.each_with_object({}) do |(key, value), normalized|
|
46
|
+
normalized[key] = case value
|
47
|
+
when BlueprintsBoy::Dependency
|
48
|
+
environment.instance_eval(&value)
|
49
|
+
else
|
50
|
+
value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_context(dependencies, attributes, factory_class)
|
56
|
+
@context = @context.chain(dependencies, attributes, factory_class)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module BlueprintsBoy
|
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
|
9
|
+
class Configuration
|
10
|
+
# Allows passing custom filename pattern in case blueprints are held in place other than spec/blueprint, test/blueprint, blueprint.
|
11
|
+
attr_reader :filenames
|
12
|
+
# Allows passing scenarios that should be prebuilt and available in all tests. Works similarly to fixtures.
|
13
|
+
attr_accessor :prebuild
|
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.
|
15
|
+
attr_reader :root
|
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.
|
17
|
+
attr_accessor :transactions
|
18
|
+
# Define global blueprints that are prebuilt in all tests
|
19
|
+
attr_reader :global
|
20
|
+
|
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.
|
24
|
+
def initialize
|
25
|
+
self.filenames = [nil, "spec", "test"].map do |dir|
|
26
|
+
['blueprints.rb', 'blueprints/*.rb'].map do |pattern|
|
27
|
+
File.join([dir, pattern].compact)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@prebuild = []
|
31
|
+
@transactions = true
|
32
|
+
@root = defined?(Rails) ? Rails.root : Pathname.pwd
|
33
|
+
@global = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def filenames=(value)
|
37
|
+
@filenames = Array(value).flatten.collect { |path| Pathname.new(path) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def root=(value)
|
41
|
+
@root = Pathname.new(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def global=(value)
|
45
|
+
@global = Array(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module BlueprintsBoy
|
2
|
+
class Context
|
3
|
+
attr_accessor :dependencies, :attrs, :factory_class
|
4
|
+
|
5
|
+
def initialize(file_name, &added_callback)
|
6
|
+
@file_name = file_name
|
7
|
+
@added_callback = added_callback
|
8
|
+
@dependencies = []
|
9
|
+
@attrs = {}
|
10
|
+
@factory_class = nil
|
11
|
+
instance_eval(File.read(file_name), file_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def depends_on(*dependencies, &block)
|
15
|
+
chain(dependencies, nil, nil, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes(attributes, &block)
|
19
|
+
chain(nil, attributes, nil, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def factory(factory_class, &block)
|
23
|
+
chain(nil, nil, factory_class, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def chain(dependencies, attributes, factory, &block)
|
27
|
+
dup.tap do |context|
|
28
|
+
context.dependencies |= dependencies if dependencies
|
29
|
+
context.attrs = context.attrs.merge(attributes) if attributes
|
30
|
+
context.factory_class = factory if factory
|
31
|
+
context.instance_eval(&block) if block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def blueprint(*args, &block)
|
36
|
+
Blueprint.new(self, *args, &block).tap do |blueprint|
|
37
|
+
@added_callback.call blueprint if @added_callback
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def group(groups)
|
42
|
+
groups.collect do |name, children|
|
43
|
+
blueprint(name) { build(*children) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def dependency(name, *args, &block)
|
48
|
+
BlueprintsBoy::Dependency.new(name, *args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :method_missing, :dependency
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class BlueprintsBoy::Dependency < BasicObject
|
2
|
+
def initialize(name, *args)
|
3
|
+
@name = name
|
4
|
+
@options = args.extract_options!
|
5
|
+
@blueprint_name = args.first || @name
|
6
|
+
@registry = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_proc
|
10
|
+
name, options, registry, blueprint_name = @name, @options, @registry, @blueprint_name
|
11
|
+
::Proc.new do
|
12
|
+
build blueprint_name => options
|
13
|
+
registry.inject(blueprint_data(name)) do |value, (method, args, block)|
|
14
|
+
value.send(method, *args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method, *args, &block)
|
20
|
+
@registry << [method, args, block]
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BlueprintsBoy
|
2
|
+
class Factories
|
3
|
+
def initialize
|
4
|
+
@factories = Hash.new { |hash, key| hash[key] = {} }
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(factory_class, strategy, &block)
|
8
|
+
@factories[factory_class][strategy] = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](factory_class, strategy)
|
12
|
+
factory_class.ancestors.each do |ancestor|
|
13
|
+
return @factories[ancestor][strategy] if @factories.key?(ancestor)
|
14
|
+
end
|
15
|
+
raise FactoryNotFound, "Factory for #{factory_class} can't be located"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module BlueprintsBoy::Helper
|
2
|
+
def set(name, value)
|
3
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
4
|
+
def self.#{name}
|
5
|
+
@_blueprint_data[:#{name}]
|
6
|
+
end
|
7
|
+
RUBY
|
8
|
+
@_blueprint_data[name] = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def autoset(name, value)
|
12
|
+
if respond_to?(name)
|
13
|
+
@_blueprint_data[name] ||= value
|
14
|
+
else
|
15
|
+
set(name, value)
|
16
|
+
end
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
def blueprint_data(name = nil)
|
21
|
+
if name
|
22
|
+
@_blueprint_data[name]
|
23
|
+
else
|
24
|
+
@_blueprint_data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def build(*names)
|
29
|
+
build_with nil, *names
|
30
|
+
end
|
31
|
+
|
32
|
+
def build!(*names)
|
33
|
+
build_with :create, *names
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_new(*names)
|
37
|
+
build_with :new, *names
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_attributes(*names)
|
41
|
+
build_with :attributes, *names
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_with(strategy, *names)
|
45
|
+
BlueprintsBoy.manager.build(self, names, strategy: strategy)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
if ActiveRecord::VERSION::MAJOR < 4
|
2
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :create) { |data| data.factory.create!(data.attributes, without_protection: true) }
|
3
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :new) { |data| data.factory.new(data.attributes, without_protection: true) }
|
4
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :update) { |data| blueprint_data(data.name).update_attributes!(data.options, without_protection: true) }
|
5
|
+
else
|
6
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :create) { |data| data.factory.create!(data.attributes) }
|
7
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :new) { |data| data.factory.new(data.attributes) }
|
8
|
+
BlueprintsBoy.factories.add(ActiveRecord::Base, :update) { |data| blueprint_data(data.name).update_attributes!(data.options) }
|
9
|
+
end
|
10
|
+
|
11
|
+
DatabaseCleaner[:active_record].strategy = BlueprintsBoy.config.transactions ? :transaction : :truncation
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module BlueprintsBoy::MinitestHelper
|
2
|
+
def before_setup
|
3
|
+
super
|
4
|
+
BlueprintsBoy.manager.setup(self)
|
5
|
+
end
|
6
|
+
|
7
|
+
def after_teardown
|
8
|
+
super
|
9
|
+
BlueprintsBoy.manager.teardown
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def build(*args)
|
14
|
+
before do
|
15
|
+
build(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
MiniTest::Unit::TestCase.send :include, BlueprintsBoy::Helper, BlueprintsBoy::MinitestHelper if defined?(MiniTest::Unit::TestCase)
|
22
|
+
MiniTest::Test.send :include, BlueprintsBoy::Helper, BlueprintsBoy::MinitestHelper if defined?(MiniTest::Test)
|
23
|
+
MiniTest::Spec.send :extend, BlueprintsBoy::MinitestHelper::ClassMethods if defined?(MiniTest::Spec)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
if Mongoid::VERSION.to_i < 4
|
2
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :create) { |data| data.factory.create!(data.attributes, without_protection: true) }
|
3
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :new) { |data| data.factory.new(data.attributes, without_protection: true) }
|
4
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :update) { |data| blueprint_data(data.name).update_attributes!(data.options, without_protection: true) }
|
5
|
+
else
|
6
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :create) { |data| data.factory.create!(data.attributes) }
|
7
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :new) { |data| data.factory.new(data.attributes) }
|
8
|
+
BlueprintsBoy.factories.add(Mongoid::Document, :update) { |data| blueprint_data(data.name).update_attributes!(data.options) }
|
9
|
+
end
|
10
|
+
|
11
|
+
DatabaseCleaner[:mongoid].strategy = :truncation
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module BlueprintsBoy::RSpecHelper
|
2
|
+
def build(*args)
|
3
|
+
before do
|
4
|
+
build(*args)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.include BlueprintsBoy::Helper
|
11
|
+
config.extend BlueprintsBoy::RSpecHelper
|
12
|
+
|
13
|
+
config.before do
|
14
|
+
BlueprintsBoy.manager.setup(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
config.after do
|
18
|
+
BlueprintsBoy.manager.teardown
|
19
|
+
end
|
20
|
+
end if RSpec.respond_to?(:configure) # Otherwise db:seed might crash
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module BlueprintsBoy
|
2
|
+
class Manager
|
3
|
+
attr_reader :blueprints, :registry
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@blueprints = {}
|
7
|
+
@registry = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(blueprint)
|
11
|
+
@blueprints[blueprint.name] = blueprint
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(name)
|
15
|
+
@blueprints[name] or raise BlueprintNotFound, "Blueprint :#{name} cannot be found"
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :[], :find
|
19
|
+
|
20
|
+
def build(environment, names, options = {})
|
21
|
+
result = parse_names(names).collect do |name, attributes|
|
22
|
+
build_blueprint(environment, name, attributes, options)
|
23
|
+
end
|
24
|
+
result.size > 1 ? result : result.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup(environment)
|
28
|
+
prepare_env(environment)
|
29
|
+
push_registry
|
30
|
+
@registry.restore.each { |name, value| environment.set name, value }
|
31
|
+
|
32
|
+
DatabaseCleaner.start
|
33
|
+
rescue DatabaseCleaner::NoORMDetected
|
34
|
+
# ignored
|
35
|
+
end
|
36
|
+
|
37
|
+
def teardown
|
38
|
+
@registry = @registry.parent
|
39
|
+
DatabaseCleaner.clean
|
40
|
+
rescue DatabaseCleaner::NoORMDetected
|
41
|
+
# ignored
|
42
|
+
end
|
43
|
+
|
44
|
+
def push_registry(blueprint_names = [])
|
45
|
+
@registry = Registry.new(blueprint_names, @registry)
|
46
|
+
|
47
|
+
environment = Object.new
|
48
|
+
environment.singleton_class.send(:include, BlueprintsBoy::Helper)
|
49
|
+
prepare_env(environment)
|
50
|
+
build(environment, @registry.names)
|
51
|
+
@registry.store environment.blueprint_data
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def prepare_env(environment)
|
57
|
+
environment.instance_variable_set(:@_blueprint_data, {})
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_blueprint(environment, name, attributes, options)
|
61
|
+
strategy = options[:strategy] || default_strategy_for(name, attributes)
|
62
|
+
return environment.blueprint_data(name) if strategy.nil? # Blueprint is already built
|
63
|
+
|
64
|
+
@registry.built << name
|
65
|
+
blueprint = find(name)
|
66
|
+
build environment, blueprint.context.dependencies if blueprint.context.dependencies.present?
|
67
|
+
blueprint.build(environment, strategy, attributes || {})
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_strategy_for(name, attributes)
|
71
|
+
if @registry.built.include?(name)
|
72
|
+
if attributes.present?
|
73
|
+
:update
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
else
|
78
|
+
:create
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_names(names)
|
83
|
+
names_with_options = names.extract_options!
|
84
|
+
names.push(*names_with_options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class BlueprintsBoy::Railtie < Rails::Railtie
|
2
|
+
class Seeder
|
3
|
+
def initialize(parent)
|
4
|
+
@parent = parent
|
5
|
+
end
|
6
|
+
|
7
|
+
def load_seed
|
8
|
+
BlueprintsBoy.enable
|
9
|
+
|
10
|
+
TOPLEVEL_BINDING.eval('self').instance_eval do
|
11
|
+
@_blueprint_data = {}
|
12
|
+
include BlueprintsBoy::Helper
|
13
|
+
end
|
14
|
+
|
15
|
+
@parent.load_seed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer 'blueprints_boy.set_seed_loader' do
|
20
|
+
if defined?(ActiveRecord::Tasks::DatabaseTasks)
|
21
|
+
ActiveRecord::Tasks::DatabaseTasks.seed_loader = Seeder.new(ActiveRecord::Tasks::DatabaseTasks.seed_loader)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module BlueprintsBoy
|
2
|
+
class Registry
|
3
|
+
attr_reader :built, :parent, :names
|
4
|
+
|
5
|
+
def initialize(names, parent)
|
6
|
+
@names = names
|
7
|
+
@parent = parent
|
8
|
+
@built = Set.new
|
9
|
+
@built.merge(parent.built) if parent
|
10
|
+
|
11
|
+
@stored = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def store(data)
|
15
|
+
data.each do |name, value|
|
16
|
+
@stored[name] = Marshal.dump(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def restore
|
21
|
+
data = @stored.each_with_object({}) do |(name, dump), result|
|
22
|
+
result[name] = Marshal.load(dump)
|
23
|
+
end
|
24
|
+
data.reverse_merge!(@parent.restore) if @parent
|
25
|
+
data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
5
|
+
require 'database_cleaner'
|
6
|
+
require 'blueprints_boy/version'
|
7
|
+
require 'blueprints_boy/railtie' if defined?(Rails)
|
8
|
+
|
9
|
+
module BlueprintsBoy
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
|
12
|
+
autoload :BlueprintNotFound, 'blueprints_boy/errors'
|
13
|
+
autoload :FactoryNotFound, 'blueprints_boy/errors'
|
14
|
+
autoload :StrategyNotFound, 'blueprints_boy/errors'
|
15
|
+
|
16
|
+
autoload :Configuration
|
17
|
+
autoload :Helper
|
18
|
+
autoload :Context
|
19
|
+
autoload :Manager
|
20
|
+
autoload :Factories
|
21
|
+
autoload :Blueprint
|
22
|
+
autoload :Dependency
|
23
|
+
autoload :Registry
|
24
|
+
|
25
|
+
def self.enable
|
26
|
+
yield config if block_given?
|
27
|
+
require 'blueprints_boy/integration/minitest' if defined?(MiniTest)
|
28
|
+
require 'blueprints_boy/integration/rspec' if defined?(RSpec)
|
29
|
+
require 'blueprints_boy/integration/cucumber' if defined?(Cucumber)
|
30
|
+
require 'blueprints_boy/integration/active_record' if defined?(ActiveRecord)
|
31
|
+
require 'blueprints_boy/integration/mongoid' if defined?(Mongoid)
|
32
|
+
prepare
|
33
|
+
manager.push_registry(config.global)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.prepare
|
37
|
+
read_files
|
38
|
+
DatabaseCleaner.clean_with(:truncation)
|
39
|
+
rescue DatabaseCleaner::NoORMDetected
|
40
|
+
# ignored
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.read_files
|
44
|
+
config.filenames.each do |pattern|
|
45
|
+
Dir[config.root.join(pattern)].each do |file_name|
|
46
|
+
Context.new(file_name) { |blueprint| manager.add(blueprint) } if File.file?(file_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.manager
|
52
|
+
@manager ||= Manager.new
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.config
|
56
|
+
@config ||= Configuration.new
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.factories
|
60
|
+
@factories ||= Factories.new
|
61
|
+
end
|
62
|
+
end
|