josevalim-nested_scenarios 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Thomas Preston-Werner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,168 @@
1
+ NestedScenarios v0.1
2
+ ====================
3
+
4
+ This plugin is based on FixtureScenarios and FixtureScenarioBuilder.
5
+ It includes both worlds in just one plugin with some fixes, new features
6
+ and Rails 2.2 support.
7
+
8
+ You can check them at:
9
+
10
+ FixtureScenarios
11
+ Info: http://code.google.com/p/fixture-scenarios/
12
+ SVN : http://fixture-scenarios.googlecode.com/svn/trunk/fixture_scenarios
13
+
14
+ FixtureScenariosBuilder
15
+ Info: http://errtheblog.com/post/7708
16
+ SVN : svn://errtheblog.com/svn/plugins/fixture_scenarios_builder
17
+
18
+ NestedScenarios
19
+ Info: http://josevalim.blogspot.com/
20
+ Git : http://github.com/josevalim/nested_scenarios
21
+
22
+ == Install
23
+
24
+ Install NestedScenarios is very easy. It is stored in GitHub, so if you have
25
+ never installed a gem via GitHub run the following:
26
+
27
+ gem sources -a http://gems.github.com
28
+
29
+ Then install the gem:
30
+
31
+ sudo gem install josevalim-nested_scenarios
32
+
33
+ In RAILS_ROOT/config/environment.rb:
34
+
35
+ config.gem "josevalim-nested_scenarios", :lib => "nested_scenarios", :source => "http://gems.github.com"
36
+
37
+ == Why
38
+
39
+ You may, from time to time, wish to build your fixtures entirely in Ruby.
40
+ Doing so has its advantages, such as automatically created join tables
41
+ and default attributes. YAML files, however, bring with them some real
42
+ nice features in Rails which are difficult to abandon: transactional fixtures,
43
+ table_name(:key) helpers, and auto-clearing between tests. How does one get
44
+ the best of both worlds?
45
+
46
+ == Usage
47
+
48
+ Using the +scenario+ method within <tt>scenarios.rb</tt> file,
49
+ FixtureScenariosBuilder can create your YAML fixture scenarios automatically
50
+ at run time from Ruby-created fixtures.
51
+
52
+ Any file inside the +fixture_path+ called scenarios.rb is loaded to
53
+ generating scenarios:
54
+
55
+ [RAILS_ROOT]
56
+ +-test/
57
+ +-fixtures/
58
+ +-scenarios.rb
59
+
60
+ Or:
61
+
62
+ [RAILS_ROOT]
63
+ +-spec/
64
+ +-fixtures/
65
+ +-models/
66
+ +-scenarios.rb
67
+ +-controllers/
68
+ +-scenarios.rb
69
+ +-helpers/
70
+ +-scenarios.rb
71
+
72
+ Now build your scenarios in those file, wrapping scenarios in the
73
+ +scenario+ method and providing it with the name of your scenario.
74
+ A brief example of a complete <tt>scenarios.rb</tt> file:
75
+
76
+ scenario :banned_users do
77
+ %w( Tom Chris Kevin ).each_with_index do |user, index|
78
+ User.create(:name => user, :banned => index.odd?)
79
+ end
80
+ end
81
+
82
+ Assuming +banned+ is a boolean field, this will create for us:
83
+
84
+ [RAILS_ROOT]
85
+ +-test/
86
+ +-fixtures/
87
+ +-banned_users/
88
+ +-users.yml
89
+
90
+ Our generated <tt>users.yml</tt> file will look something like this:
91
+
92
+ chris:
93
+ name: Chris
94
+ id: "2"
95
+ banned: "1"
96
+ updated_at: 2007-05-09 09:08:04
97
+ created_at: 2007-05-09 09:08:04
98
+ kevin:
99
+ name: Kevin
100
+ id: "3"
101
+ banned: "0"
102
+ updated_at: 2007-05-09 09:08:04
103
+ created_at: 2007-05-09 09:08:04
104
+ tom:
105
+ name: Tom
106
+ id: "1"
107
+ banned: "0"
108
+ updated_at: 2007-05-09 09:08:04
109
+ created_at: 2007-05-09 09:08:04
110
+
111
+ Notice how the keys correspond to the user names. You can register fields that
112
+ can be used as fixtures names by:
113
+
114
+ NestedScenarios.record_name_fields << :nickname
115
+
116
+ You can assign your records to instance variables, then call +names_from_ivars+
117
+ at the conclusion of your +scenario+ block.
118
+
119
+ scenario :foo do
120
+ @small_red_widget = Widget.create(:size => 'small', :color => 'red')
121
+ @big_blue_widget = Widget.create(:size => 'big', :color => 'blue')
122
+
123
+ names_from_ivars!
124
+ end
125
+
126
+ The above produces the following YAML:
127
+
128
+ small_red_widget:
129
+ size: small
130
+ color: red
131
+ updated_at: 2007-12-27 10:09:05
132
+ created_at: 2007-12-27 10:09:05
133
+ big_blue_widget:
134
+ size: big
135
+ color: blue
136
+ updated_at: 2007-12-27 10:19:23
137
+ created_at: 2007-12-27 10:19:23
138
+
139
+ To build the scenario you have to run:
140
+
141
+ rake db:build:scenario
142
+
143
+ In NestedScenarios, scenarios are not generated automatically. Another change
144
+ is how scenarios are nested:
145
+
146
+ scenario :models => { :users => :banned } do
147
+ User.create(:name => 'Kevin', :banned => true)
148
+ end
149
+
150
+ This will create an YAML in the following dir:
151
+
152
+ [RAILS_ROOT]
153
+ +-test/
154
+ +-fixtures/
155
+ +-models/
156
+ +-users/
157
+ +-banned/
158
+ +-users.yml
159
+
160
+ Finally, you can choose which scenario to use in your tests by:
161
+
162
+ scenario :users
163
+
164
+ Or, in the case of nested scenarios:
165
+
166
+ scenario :models => { :users => :banned }
167
+
168
+ If no scenario is sent, the default behaviour is adopted.
@@ -0,0 +1,12 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Generate documentation for Footnotes plugin.'
6
+ Rake::RDocTask.new(:rdoc) do |rdoc|
7
+ rdoc.rdoc_dir = 'rdoc'
8
+ rdoc.title = 'NestedScenarios'
9
+ rdoc.options << '--line-numbers' << '--inline-source'
10
+ rdoc.rdoc_files.include('README')
11
+ rdoc.rdoc_files.include('lib/**/*.rb')
12
+ end
data/init.rb ADDED
@@ -0,0 +1,11 @@
1
+ if RAILS_ENV == 'test'
2
+ require 'test/unit/testcase'
3
+ require 'test/unit/testsuite'
4
+
5
+ require 'active_record/fixtures'
6
+
7
+ require File.join(File.dirname(__FILE__), 'lib', 'join')
8
+ require File.join(File.dirname(__FILE__), 'lib', 'nested_scenarios')
9
+ require File.join(File.dirname(__FILE__), 'lib', 'builder')
10
+ require File.join(File.dirname(__FILE__), 'lib', 'fixtures')
11
+ end
@@ -0,0 +1,131 @@
1
+ require 'fileutils'
2
+
3
+ class Object
4
+ def scenario(scenario, &block)
5
+ if block.nil?
6
+ raise NoMethodError, "undefined method `scenario' for #{inspect}"
7
+ else
8
+ NestedScenarios::Builder.new(scenario, &block).build
9
+ end
10
+ end
11
+
12
+ alias_method :build_scenario, :scenario
13
+ end
14
+
15
+ class NestedScenarios::Builder
16
+ @@select_sql = "SELECT * FROM %s"
17
+
18
+ def initialize(scenario, &block)
19
+ case scenario
20
+ when Hash
21
+ @scenario = scenario.join('/')
22
+ when Symbol, String
23
+ @scenario = scenario.to_s
24
+ else
25
+ raise "I don't know how to build `#{scenario.inspect}'"
26
+ end
27
+
28
+ @block = block
29
+ @custom_names = {}
30
+ end
31
+
32
+ def build
33
+ say "Building scenario `#{@scenario}'"
34
+ NestedScenarios.delete_tables
35
+
36
+ surface_errors { instance_eval(&@block) }
37
+ FileUtils.mkdir_p self.class.fixtures_dir(@scenario)
38
+
39
+ dump_tables
40
+ end
41
+
42
+ def names_from_ivars!
43
+ instance_values.each do |var, value|
44
+ name(var, value) if value.is_a? ActiveRecord::Base
45
+ end
46
+ end
47
+
48
+ def self.fixtures_dir(*paths)
49
+ File.join(RAILS_ROOT, spec_or_test_dir, 'fixtures', *paths)
50
+ end
51
+
52
+ def self.spec_or_test_dir
53
+ File.exists?(File.join(RAILS_ROOT, 'spec')) ? 'spec' : 'test'
54
+ end
55
+
56
+ def self.fixtures_dir_exists?(dir = @scenario)
57
+ File.exists? fixtures_dir(dir)
58
+ end
59
+
60
+ def self.fixture_file_exists?
61
+ File.exists? fixture_file
62
+ end
63
+
64
+ protected
65
+ def say(*messages)
66
+ puts messages.map { |message| "=> #{message}" }
67
+ end
68
+
69
+ def write_fixture_file(fixture_data)
70
+ File.open(fixture_file, 'w') do |file|
71
+ file.write fixture_data.to_yaml
72
+ end
73
+ end
74
+
75
+ def fixture_file
76
+ self.class.fixtures_dir(@scenario, "#{@table_name}.yml")
77
+ end
78
+
79
+ def surface_errors
80
+ yield
81
+ rescue Object => error
82
+ puts
83
+ say "There was an error building scenario `#{@scenario}'", error.inspect
84
+ puts
85
+ puts error.backtrace
86
+ puts
87
+ exit!
88
+ end
89
+
90
+ def name(custom_name, model_object)
91
+ key = [model_object.class.name, model_object.id]
92
+ @custom_names[key] = custom_name
93
+ model_object
94
+ end
95
+
96
+ def record_name(record_hash)
97
+ key = [@table_name.classify, record_hash['id'].to_i]
98
+ @record_names << (name = @custom_names[key] || inferred_record_name(record_hash) )
99
+ name
100
+ end
101
+
102
+ def inferred_record_name(record_hash)
103
+ NestedScenarios.record_name_fields.each do |try|
104
+ if name = record_hash[try]
105
+ inferred_name = name.underscore.gsub(/\W/, ' ').squeeze(' ').tr(' ', '_')
106
+ count = @record_names.select { |name| name == inferred_name }.size
107
+ return count.zero? ? inferred_name : "#{inferred_name}_#{count}"
108
+ end
109
+ end
110
+
111
+ "#{@table_name}_#{@row_index.succ!}"
112
+ end
113
+
114
+ def dump_tables
115
+ fixtures = NestedScenarios.tables.inject([]) do |files, @table_name|
116
+ rows = ActiveRecord::Base.connection.select_all(@@select_sql % @table_name)
117
+ next files if rows.empty?
118
+
119
+ @row_index = '000'
120
+ @record_names = []
121
+ fixture_data = rows.inject({}) do |hash, record|
122
+ hash.merge(record_name(record) => record)
123
+ end
124
+
125
+ write_fixture_file fixture_data
126
+
127
+ files + [File.basename(fixture_file)]
128
+ end
129
+ say "Built scenario `#{@scenario}' with #{fixtures.to_sentence}"
130
+ end
131
+ end
@@ -0,0 +1,72 @@
1
+ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
2
+ def self.destroy_fixtures(table_names)
3
+ NestedScenarios.delete_tables(table_names)
4
+ end
5
+ end
6
+
7
+ module Test #:nodoc:
8
+ module Unit #:nodoc:
9
+ class TestCase #:nodoc:
10
+ superclass_delegating_accessor :scenario_path
11
+
12
+ def self.scenario(scenario_name = nil, options = {})
13
+ case scenario_name
14
+ when Hash
15
+ scenario_name = scenario_name.join('/')
16
+ when Symbol, String
17
+ scenario_name = scenario_name.to_s
18
+ end
19
+
20
+ self.scenario_path = "#{self.fixture_path}/#{scenario_name}/" if scenario_name
21
+
22
+ self.fixtures(:all)
23
+ end
24
+
25
+ def self.fixtures(*table_names)
26
+ if table_names.first == :all
27
+ table_names = Dir["#{self.scenario_path || self.fixture_path}/*.yml"]
28
+ table_names += Dir["#{self.scenario_path || self.fixture_path}/*.csv"]
29
+ table_names.map! { |f| File.basename(f).split('.')[0..-2].join('.') }
30
+ else
31
+ table_names = table_names.flatten.map { |n| n.to_s }
32
+ end
33
+
34
+ self.fixture_table_names |= table_names
35
+
36
+ require_fixture_classes(table_names)
37
+ setup_fixture_accessors(table_names)
38
+ end
39
+
40
+ private
41
+ def load_fixtures
42
+ @loaded_fixtures = {}
43
+ fixtures = Fixtures.create_fixtures(self.scenario_path || self.fixture_path, fixture_table_names, fixture_class_names)
44
+ unless fixtures.nil?
45
+ if fixtures.instance_of?(Fixtures)
46
+ @loaded_fixtures[fixtures.table_name] = fixtures
47
+ else
48
+ fixtures.each { |f| @loaded_fixtures[f.table_name] = f }
49
+ end
50
+ end
51
+ end
52
+
53
+ def teardown_fixtures
54
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
55
+
56
+ Fixtures.destroy_fixtures(self.fixture_table_names)
57
+
58
+ unless use_transactional_fixtures?
59
+ Fixtures.reset_cache
60
+ end
61
+
62
+ # Rollback changes if a transaction is active.
63
+ if use_transactional_fixtures? && ActiveRecord::Base.connection.open_transactions != 0
64
+ ActiveRecord::Base.connection.rollback_db_transaction
65
+ ActiveRecord::Base.connection.decrement_open_transactions
66
+ end
67
+
68
+ ActiveRecord::Base.clear_active_connections!
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Hash #:nodoc:
4
+ module Join
5
+
6
+ # Returns a string created by converting each element of the hash to
7
+ # a string, separated by <i>sep</i>.
8
+ #
9
+ # { :a => :b, :c => :d }.join #=> "abcd"
10
+ # { :a => :b, :c => :d }.join('-') #=> "a-b-c-d"
11
+ #
12
+ # If the hash has a key or value as a hash also, it's also joined
13
+ # (just as it works with arrays).
14
+ #
15
+ # { :a => { :b => :c } }.join(' ') #=> "a b c"
16
+ #
17
+ # Note: hash order is just preserved in Ruby 1.9
18
+ #
19
+ def join(sep = $,)
20
+ array = []
21
+ sep = sep.to_s
22
+
23
+ self.each_pair do |k, v|
24
+ array << (k.is_a?(Hash) ? k.join(sep) : k.to_s)
25
+ array << (v.is_a?(Hash) ? v.join(sep) : v.to_s)
26
+ end
27
+
28
+ array.join(sep)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Hash.__send__ :include, ActiveSupport::CoreExtensions::Hash::Join
@@ -0,0 +1,20 @@
1
+ class NestedScenarios
2
+ cattr_accessor :record_name_fields, :skip_tables
3
+ @@record_name_fields = %w( name username title )
4
+ @@skip_tables = %w( schema_migrations )
5
+
6
+ def self.delete_tables(table_names = self.tables)
7
+ connection = ActiveRecord::Base.connection
8
+ ActiveRecord::Base.silence do
9
+ connection.disable_referential_integrity do
10
+ (table_names - @@skip_tables).each do |table_name|
11
+ connection.delete "DELETE FROM #{table_name}", 'Fixture Delete'
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.tables
18
+ ActiveRecord::Base.connection.tables - @@skip_tables
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ namespace :db do
2
+ namespace :scenario do
3
+ desc 'Build scenarios in test environment'
4
+ task :build do
5
+ # Load environment using test environment
6
+ ENV['RAILS_ENV'] = RAILS_ENV = 'test'
7
+ Rake::Task['environment'].invoke
8
+
9
+ # Force ActionMailer to delivery method :test
10
+ # while building scenarios
11
+ ActionMailer::Base.delivery_method = :test
12
+
13
+ Dir.glob(NestedScenarios::Builder.fixtures_dir + '/**/scenarios.rb').each do |scenario_rb|
14
+ puts "Reading #{scenario_rb.gsub(RAILS_ROOT, '')} scenario file:"
15
+ require scenario_rb
16
+ puts
17
+ end
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: josevalim-nested_scenarios
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - "Jos\xC3\xA9 Valim"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-29 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: FixtureScenarios, FixtureScenariosBuilder, Yaml and Ruby in one big mix for Rails
17
+ email: jose.valim@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - MIT-LICENSE
26
+ - README
27
+ - Rakefile
28
+ - init.rb
29
+ - lib/join.rb
30
+ - lib/fixtures.rb
31
+ - lib/nested_scenarios.rb
32
+ - lib/builder.rb
33
+ - tasks/builder_tasks.rake
34
+ has_rdoc: true
35
+ homepage: http://github.com/josevalim/nested_scenarios
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --main
39
+ - README
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: FixtureScenarios, FixtureScenariosBuilder, Yaml and Ruby in one big mix for Rails
61
+ test_files: []
62
+