blueprints 0.6.3 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +65 -66
- data/Rakefile +4 -0
- data/VERSION +1 -1
- data/bin/blueprintify +80 -0
- data/blueprints.gemspec +21 -5
- data/lib/blueprints.rb +26 -51
- data/lib/blueprints/{plan.rb → blueprint.rb} +4 -4
- data/lib/blueprints/buildable.rb +2 -2
- data/lib/blueprints/configuration.rb +42 -0
- data/lib/blueprints/convertable.rb +30 -0
- data/lib/blueprints/convertable/fixtures.rb +43 -0
- data/lib/blueprints/database_backends/active_record.rb +44 -85
- data/lib/blueprints/errors.rb +3 -3
- data/lib/blueprints/extensions/deprecated.rb +22 -0
- data/lib/blueprints/extensions/rspec.rb +7 -32
- data/lib/blueprints/extensions/test_unit.rb +9 -18
- data/lib/blueprints/file_context.rb +3 -3
- data/lib/blueprints/helper.rb +7 -5
- data/lib/blueprints/namespace.rb +2 -2
- data/lib/blueprints/root_namespace.rb +13 -9
- data/spec/active_record/blueprints_spec.rb +7 -49
- data/spec/active_record/spec_helper.rb +6 -1
- data/spec/no_db/spec_helper.rb +6 -1
- data/spec/test_all.sh +32 -0
- data/spec/unit/configuration_spec.rb +34 -0
- data/spec/unit/spec_helper.rb +17 -0
- data/test/blueprints_test.rb +7 -49
- data/test/test_helper.rb +6 -2
- metadata +51 -12
- data/lib/blueprints/database_backends/abstract.rb +0 -21
- data/lib/blueprints/database_backends/none.rb +0 -18
@@ -1,15 +1,15 @@
|
|
1
1
|
module Blueprints
|
2
2
|
# Class for actual blueprints. Allows building itself by executing block passed against current context.
|
3
|
-
class
|
3
|
+
class Blueprint < Buildable
|
4
4
|
# Initializes blueprint by name and block
|
5
5
|
def initialize(name, &block)
|
6
6
|
super(name)
|
7
7
|
@block = block
|
8
8
|
end
|
9
9
|
|
10
|
-
# Builds
|
10
|
+
# Builds blueprint and adds it to executed blueprint hash. Setups instance variable with same name as blueprint if it is not defined yet.
|
11
11
|
def build_self(build_once = true)
|
12
|
-
if build_once and Namespace.root.
|
12
|
+
if build_once and Namespace.root.executed_blueprints.include?(path)
|
13
13
|
Blueprints.warn("Building with options, but blueprint was already built", @name) if Namespace.root.context.options.present?
|
14
14
|
else
|
15
15
|
surface_errors do
|
@@ -19,7 +19,7 @@ module Blueprints
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
Namespace.root.
|
22
|
+
Namespace.root.executed_blueprints << path
|
23
23
|
@result
|
24
24
|
end
|
25
25
|
|
data/lib/blueprints/buildable.rb
CHANGED
@@ -74,7 +74,7 @@ module Blueprints
|
|
74
74
|
@parents.each do |p|
|
75
75
|
parent = begin
|
76
76
|
namespace[p]
|
77
|
-
rescue
|
77
|
+
rescue BlueprintNotFoundError
|
78
78
|
Namespace.root[p]
|
79
79
|
end
|
80
80
|
|
@@ -90,7 +90,7 @@ module Blueprints
|
|
90
90
|
name = name.to_sym unless name == ''
|
91
91
|
return name, []
|
92
92
|
else
|
93
|
-
raise TypeError, "Pass
|
93
|
+
raise TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint #{name.inspect}"
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Blueprints
|
2
|
+
class Configuration
|
3
|
+
SUPPORTED_ORMS = [nil, :active_record]
|
4
|
+
# Allows passing custom filename pattern in case blueprints are held in place other than spec/blueprint, test/blueprint, blueprint.
|
5
|
+
attr_accessor :filename
|
6
|
+
# Allows passing scenarios that should be prebuilt and available in all tests. Works similarly to fixtures.
|
7
|
+
attr_accessor :prebuild
|
8
|
+
# Allows passing custom root folder to use in case of non rails project. Defaults to RAILS_ROOT or current folder if RAILS_ROOT is not defined.
|
9
|
+
attr_accessor :root
|
10
|
+
# 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
|
+
attr_accessor :transactions
|
12
|
+
# Returns ORM that is used, default is :active_record
|
13
|
+
attr_reader :orm
|
14
|
+
|
15
|
+
# Sets default attributes for all attributes
|
16
|
+
def initialize
|
17
|
+
@filename = [nil, "spec", "test"].map do |dir|
|
18
|
+
["blueprint"].map do |file|
|
19
|
+
path = File.join([dir, file].compact)
|
20
|
+
["#{path}.rb", File.join(path, "*.rb")]
|
21
|
+
end
|
22
|
+
end.flatten
|
23
|
+
@orm = :active_record
|
24
|
+
@prebuild = []
|
25
|
+
@transactions = true
|
26
|
+
@root = if defined?(RAILS_ROOT)
|
27
|
+
RAILS_ROOT
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Allows specifying what ORM should be used. See SUPPORTED_ORMS to check what values it can contain.
|
34
|
+
def orm=(value)
|
35
|
+
if SUPPORTED_ORMS.include?(value)
|
36
|
+
@orm = value
|
37
|
+
else
|
38
|
+
raise ArgumentError, "Unsupported ORM #{value.inspect}. Blueprints supports only #{SUPPORTED_ORMS.collect(&:inspect).join(', ')}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Blueprints
|
2
|
+
module Convertable
|
3
|
+
def persist(str)
|
4
|
+
File.open(@output_file, 'a+') do |f|
|
5
|
+
f.write(str)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def process!
|
10
|
+
persist "#{banner_start}#{convert}#{banner_end}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def banner_start
|
14
|
+
"### blueprints from #{@format}\n\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def banner_end
|
18
|
+
"\n###\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Converter
|
23
|
+
def self.for(format, options = {})
|
24
|
+
require "blueprints/convertable/#{format}"
|
25
|
+
Class.new("Blueprints::#{format.capitalize}Converter".constantize) do |options|
|
26
|
+
include Convertable
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Blueprints
|
2
|
+
class FixturesConverter
|
3
|
+
def initialize(options = {})
|
4
|
+
@format = "fixtures"
|
5
|
+
@source_files = options[:source_files]
|
6
|
+
@output_file = options[:output_file]
|
7
|
+
@blueprints_data = ""
|
8
|
+
|
9
|
+
raise "No source files given" unless @source_files
|
10
|
+
raise "No output file given" unless @output_file
|
11
|
+
end
|
12
|
+
|
13
|
+
def convert
|
14
|
+
@source_files.each do |fixture_file|
|
15
|
+
klass = File.basename(fixture_file, '.yml').singularize.capitalize
|
16
|
+
|
17
|
+
loaded_yaml = YAML.load(File.read(fixture_file))
|
18
|
+
|
19
|
+
@blueprints_data = loaded_yaml.collect do |title,yaml_obj|
|
20
|
+
params = yaml_obj.collect do |k,v|
|
21
|
+
":#{k} => #{parameterize(v)}"
|
22
|
+
end.join(', ')
|
23
|
+
|
24
|
+
"#{klass}.blueprint(:#{title}, {#{params}})\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@blueprints_data
|
29
|
+
end
|
30
|
+
|
31
|
+
def parameterize(object)
|
32
|
+
if object =~ /<%=\s+?(.+)\s+%>/
|
33
|
+
'('+$1+')'
|
34
|
+
elsif object.is_a?(String)
|
35
|
+
(%Q(#{object})).inspect
|
36
|
+
elsif object.nil?
|
37
|
+
'nil'
|
38
|
+
else
|
39
|
+
object.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,94 +1,53 @@
|
|
1
1
|
module Blueprints
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# Returns all tables without skipped ones.
|
34
|
-
def tables
|
35
|
-
::ActiveRecord::Base.connection.tables - skip_tables
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns tables that should never be cleared (those that contain migrations information).
|
39
|
-
def skip_tables
|
40
|
-
%w( schema_info schema_migrations )
|
41
|
-
end
|
42
|
-
|
43
|
-
# Extensions for active record class
|
44
|
-
module ActiveRecordExtensions
|
45
|
-
module Class
|
46
|
-
# Two forms of this method can be used. First one is typically used inside blueprint block. Essentially it does
|
47
|
-
# same as <tt>create!</tt>, except it does bypass attr_protected and attr_accessible. It accepts only a hash or attributes,
|
48
|
-
# same as <tt>create!</tt> does.
|
49
|
-
# blueprint :post => :user do
|
50
|
-
# @user.posts.blueprint(:title => 'first post', :text => 'My first post')
|
51
|
-
# end
|
52
|
-
# The second form is used when you want to define new blueprint. It takes first argument as name of blueprint
|
53
|
-
# and second one as hash of attributes. As you cannot use instance variables outside of blueprint block, you need
|
54
|
-
# to prefix them with colon. So the example above could be rewritten like this:
|
55
|
-
# Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user))
|
56
|
-
# or like this:
|
57
|
-
# Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => :@user).depends_on(:user)
|
58
|
-
# or like this:
|
59
|
-
# Post.blueprint({:post => :user}, :title => 'first post', :text => 'My first post', :user => :@user)
|
60
|
-
def blueprint(name_or_attrs, attrs = {})
|
61
|
-
if Blueprints::FileContext.evaluating
|
62
|
-
klass = self
|
63
|
-
blueprint = Blueprints::Plan.new(name_or_attrs) { klass.blueprint attributes }
|
64
|
-
blueprint.depends_on(*attrs.values.select {|attr| attr.is_a?(Blueprints::Buildable::Dependency) }).attributes(attrs)
|
65
|
-
blueprint
|
66
|
-
else
|
67
|
-
if name_or_attrs.is_a?(Array)
|
68
|
-
name_or_attrs.collect { |attrs| blueprint(attrs) }
|
69
|
-
else
|
70
|
-
object = new
|
71
|
-
object.blueprint(name_or_attrs)
|
72
|
-
object
|
73
|
-
end
|
74
|
-
end
|
2
|
+
# Extensions for active record class
|
3
|
+
module ActiveRecordExtensions
|
4
|
+
module ClassMethods
|
5
|
+
# Two forms of this method can be used. First one is typically used inside blueprint block. Essentially it does
|
6
|
+
# same as <tt>create!</tt>, except it does bypass attr_protected and attr_accessible. It accepts only a hash or attributes,
|
7
|
+
# same as <tt>create!</tt> does.
|
8
|
+
# blueprint :post => :user do
|
9
|
+
# @user.posts.blueprint(:title => 'first post', :text => 'My first post')
|
10
|
+
# end
|
11
|
+
# The second form is used when you want to define new blueprint. It takes first argument as name of blueprint
|
12
|
+
# and second one as hash of attributes. As you cannot use instance variables outside of blueprint block, you need
|
13
|
+
# to prefix them with colon. So the example above could be rewritten like this:
|
14
|
+
# Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user))
|
15
|
+
# or like this:
|
16
|
+
# Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => :@user).depends_on(:user)
|
17
|
+
# or like this:
|
18
|
+
# Post.blueprint({:post => :user}, :title => 'first post', :text => 'My first post', :user => :@user)
|
19
|
+
def blueprint(name_or_attrs, attrs = {})
|
20
|
+
if Blueprints::FileContext.evaluating
|
21
|
+
klass = self
|
22
|
+
blueprint = Blueprints::Blueprint.new(name_or_attrs) { klass.blueprint attributes }
|
23
|
+
blueprint.depends_on(*attrs.values.select {|attr| attr.is_a?(Blueprints::Buildable::Dependency) }).attributes(attrs)
|
24
|
+
blueprint
|
25
|
+
else
|
26
|
+
if name_or_attrs.is_a?(Array)
|
27
|
+
name_or_attrs.collect { |attrs| blueprint(attrs) }
|
28
|
+
else
|
29
|
+
object = new
|
30
|
+
object.blueprint(name_or_attrs)
|
31
|
+
object
|
75
32
|
end
|
76
33
|
end
|
34
|
+
end
|
35
|
+
end
|
77
36
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
send(:attributes=, attributes, false)
|
87
|
-
save!
|
88
|
-
end
|
37
|
+
module InstanceMethods
|
38
|
+
# Updates attributes of object and calls save!. Bypasses attr_protected and attr_accessible.
|
39
|
+
def blueprint(attributes)
|
40
|
+
attributes.each do |attr, value|
|
41
|
+
iv_name = value.iv_name if value.is_a?(Blueprints::Buildable::Dependency)
|
42
|
+
iv_name = value if value.is_a? Symbol and value.to_s =~ /^@.+$/
|
43
|
+
attributes[attr] = Blueprints::Namespace.root.context.instance_variable_get(iv_name) if iv_name
|
89
44
|
end
|
45
|
+
send(:attributes=, attributes, false)
|
46
|
+
save!
|
90
47
|
end
|
91
|
-
|
92
48
|
end
|
93
49
|
end
|
94
50
|
end
|
51
|
+
|
52
|
+
::ActiveRecord::Base.send(:include, Blueprints::ActiveRecordExtensions::InstanceMethods)
|
53
|
+
::ActiveRecord::Base.extend(Blueprints::ActiveRecordExtensions::ClassMethods)
|
data/lib/blueprints/errors.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Blueprints
|
2
2
|
# Is raised when blueprint or namespace is not found.
|
3
|
-
class
|
3
|
+
class BlueprintNotFoundError < NameError
|
4
4
|
def initialize(*args)
|
5
|
-
@
|
5
|
+
@blueprints = args
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_s
|
9
|
-
"
|
9
|
+
"Blueprint/namespace not found '#{@blueprints.join(',')}'"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EnableBlueprints #:nodoc:
|
2
|
+
def enable_blueprints(options = {})
|
3
|
+
STDERR.puts "DEPRECATION WARNING: enable_blueprints is deprecated. Use Blueprints.enable"
|
4
|
+
Blueprints.enable do |config|
|
5
|
+
options.each {|option, value| config.send("#{option}=", value) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ActiveSupport #:nodoc:all
|
11
|
+
class TestCase
|
12
|
+
include EnableBlueprints
|
13
|
+
end
|
14
|
+
end if defined? ActiveSupport::TestCase
|
15
|
+
|
16
|
+
module Spec #:nodoc:all
|
17
|
+
module Runner
|
18
|
+
class Configuration
|
19
|
+
include EnableBlueprints
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end if defined? Spec or $0 =~ /script.spec$/
|
@@ -1,35 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def enable_blueprints(options = {})
|
7
|
-
Blueprints.load(options)
|
8
|
-
|
9
|
-
include(Blueprints::Helper)
|
10
|
-
before do
|
11
|
-
Blueprints.setup(self)
|
12
|
-
end
|
13
|
-
after do
|
14
|
-
Blueprints.teardown
|
15
|
-
end
|
1
|
+
config_class = defined?(RSpec) ? RSpec : Spec::Runner
|
2
|
+
config_class.configure do |config|
|
3
|
+
config.include(Blueprints::Helper)
|
4
|
+
config.before do
|
5
|
+
Blueprints.setup(self)
|
16
6
|
end
|
17
|
-
|
18
|
-
|
19
|
-
if defined?(RSpec)
|
20
|
-
module RSpec #:nodoc:
|
21
|
-
module Core #:nodoc:
|
22
|
-
class Configuration
|
23
|
-
include Blueprints::RspecMixin
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
else
|
28
|
-
module Spec #:nodoc:
|
29
|
-
module Runner #:nodoc:
|
30
|
-
class Configuration
|
31
|
-
include Blueprints::RspecMixin
|
32
|
-
end
|
33
|
-
end
|
7
|
+
config.after do
|
8
|
+
Blueprints.teardown
|
34
9
|
end
|
35
10
|
end
|
@@ -1,21 +1,12 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
# Enables blueprints in test/unit. Is automatically added if <tt>Spec</tt> is not defined at loading time.
|
12
|
-
# You might need to require it manually in certain case (eg. using both rspec and test/unit).
|
13
|
-
# Accepts options hash. For supported options please check Blueprints.load.
|
14
|
-
def self.enable_blueprints(options = {})
|
15
|
-
include Blueprints::Helper
|
16
|
-
Blueprints.load(options)
|
17
|
-
alias_method_chain :run, :blueprints
|
18
|
-
end
|
1
|
+
module ActiveSupport #:nodoc:all
|
2
|
+
class TestCase
|
3
|
+
include Blueprints::Helper
|
4
|
+
# Runs tests with blueprints support
|
5
|
+
def run_with_blueprints(result, &progress_block)
|
6
|
+
Blueprints.setup(self)
|
7
|
+
run_without_blueprints(result, &progress_block)
|
8
|
+
Blueprints.teardown
|
19
9
|
end
|
10
|
+
alias_method_chain :run, :blueprints
|
20
11
|
end
|
21
12
|
end
|
@@ -3,9 +3,9 @@ module Blueprints
|
|
3
3
|
module FileContext
|
4
4
|
mattr_accessor :evaluating
|
5
5
|
|
6
|
-
# Creates a new
|
7
|
-
def self.blueprint(
|
8
|
-
|
6
|
+
# Creates a new blueprint by name and block passed
|
7
|
+
def self.blueprint(name, &block)
|
8
|
+
Blueprint.new(name, &block)
|
9
9
|
end
|
10
10
|
|
11
11
|
# Creates new namespace by name, and evaluates block against it.
|
data/lib/blueprints/helper.rb
CHANGED
@@ -37,17 +37,19 @@ module Blueprints
|
|
37
37
|
#
|
38
38
|
# TODO: add sample usage
|
39
39
|
def demolish(*args)
|
40
|
+
STDERR.puts "DEPRECATION WARNING: demolish is deprecated and will be changed to support per blueprint demolishing in blueprints 0.8.0"
|
40
41
|
options = args.extract_options!
|
41
|
-
|
42
|
+
args = (ActiveRecord::Base.connection.tables - ['schema_migrations']) if args.blank?
|
43
|
+
args.each {|table| ActiveRecord::Base.connection.execute("DELETE FROM #{table}") }
|
42
44
|
|
43
45
|
if options[:undo] == :all
|
44
|
-
Namespace.root.
|
46
|
+
Namespace.root.executed_blueprints.clear
|
45
47
|
else
|
46
48
|
undo = [options[:undo]].flatten.compact.collect {|bp| bp.to_s }
|
47
|
-
unless (not_found = undo - Namespace.root.
|
48
|
-
raise(
|
49
|
+
unless (not_found = undo - Namespace.root.executed_blueprints.to_a).blank?
|
50
|
+
raise(BlueprintNotFoundError, not_found)
|
49
51
|
end
|
50
|
-
Namespace.root.
|
52
|
+
Namespace.root.executed_blueprints -= undo
|
51
53
|
end
|
52
54
|
end
|
53
55
|
end
|