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