iron_fixture_extractor 1.0.0 → 1.1.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/CHANGELOG.md +6 -0
- data/Gemfile +7 -9
- data/README.md +129 -47
- data/Rakefile +5 -6
- data/iron_fixture_extractor.gemspec +0 -3
- data/lib/fe.rb +18 -43
- data/lib/fe/extractor.rb +91 -63
- data/lib/fe/version.rb +1 -1
- data/spec/README_FOR_DEVELOPERS.md +26 -0
- data/spec/dummy_environments/sqlite/dummy1/config/database.yml +11 -0
- data/spec/dummy_environments/sqlite/dummy1/data_migrations/001_create_some_data.rb +17 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043601_create_posts.rb +13 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043602_create_authors.rb +9 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043603_create_different_posts.rb +14 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043639_create_comments.rb +13 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043808_create_group_members.rb +13 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043818_create_groups.rb +9 -0
- data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043819_create_users.rb +10 -0
- data/spec/dummy_environments/sqlite/dummy1/models/author.rb +5 -0
- data/spec/dummy_environments/sqlite/dummy1/models/comment.rb +5 -0
- data/spec/dummy_environments/sqlite/dummy1/models/complex_thing.rb +4 -0
- data/spec/dummy_environments/sqlite/dummy1/models/different_post.rb +9 -0
- data/spec/dummy_environments/sqlite/dummy1/models/group.rb +5 -0
- data/spec/dummy_environments/sqlite/dummy1/models/group_member.rb +5 -0
- data/spec/dummy_environments/sqlite/dummy1/models/post.rb +7 -0
- data/spec/dummy_environments/sqlite/dummy1/models/serialized_attribute_encoder.rb +12 -0
- data/spec/dummy_environments/sqlite/dummy1/models/user.rb +2 -0
- data/spec/dummy_environments/sqlite/dummy1/models/user/admin.rb +3 -0
- data/spec/dummy_environments/sqlite/dummy1/models/user/jerk.rb +3 -0
- data/spec/execute_extract_code_spec.rb +29 -0
- data/spec/extract_spec.rb +37 -0
- data/spec/extract_works_with_multiple_extract_queries_in_one_command_spec.rb +12 -0
- data/spec/fe_test_env_spec.rb +65 -0
- data/spec/get_hash_spec.rb +27 -0
- data/spec/load_db_map_option_spec.rb +39 -0
- data/spec/load_db_spec.rb +48 -0
- data/spec/load_db_works_with_serialized_attributes_spec.rb +18 -0
- data/spec/rebuild_spec.rb +18 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/fe_test_env.rb +181 -0
- data/spec/support/first_post_w_comments_and_authors.rb +12 -0
- data/spec/truncate_tables_for_spec.rb +39 -0
- data/spec/works_with_single_table_inheritance_spec.rb +16 -0
- data/test/basic_usage_test.ported.rb +114 -0
- data/test/{different_target_table_test.rb → different_target_table_test.not_porting.rb} +0 -0
- data/test/factory_girl_test.not_porting_now.rb +79 -0
- data/test/{fe_test_env.rb → fe_test_env.ported.rb} +0 -0
- data/test/{get_hash_test.rb → get_hash_test.ported.rb} +17 -1
- data/test/{multi_tree_usage_test.rb → multi_tree_usage_test.ported.rb} +0 -0
- data/test/{serialized_attribute_test.rb → serialized_attribute_test.ported.rb} +0 -0
- data/test/{sti_test.rb → sti_test.ported.rb} +0 -0
- data/test/test_helper.rb +0 -1
- metadata +135 -88
- data/lib/fe/factory_girl_dsl_methods.rb +0 -28
- data/test/basic_usage_test.rb +0 -106
- data/test/factory_girl_test.rb +0 -77
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
gem '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
gem 'rake'
|
11
|
-
# Specify your gem's dependencies in iron_fixture_extractor.gemspec
|
3
|
+
group :test do
|
4
|
+
gem "rspec"
|
5
|
+
gem 'sqlite3'
|
6
|
+
gem 'debugger'
|
7
|
+
gem 'shoulda'
|
8
|
+
gem 'rake'
|
9
|
+
end
|
12
10
|
gemspec
|
data/README.md
CHANGED
@@ -1,44 +1,100 @@
|
|
1
1
|
# About Iron Fixture Extractor
|
2
2
|
|
3
|
-
For extracting complex data from staging and production databases to be used for automated testing
|
3
|
+
For extracting complex data from staging and production databases to be used for automated testing.
|
4
4
|
|
5
5
|
Its best when:
|
6
|
-
* your data is too complex for factories (like when integrating with legacy systems, ERP systems, etc)
|
7
|
-
* creating and maintaining manual fixtures is cumbersome and brittle (always, :))
|
8
6
|
|
9
|
-
|
7
|
+
* your data is too complex for factories
|
8
|
+
* creating and maintaining manual fixtures is cumbersome and brittle
|
10
9
|
|
11
|
-
|
12
|
-
been built up by the development team, users, or business analysts to be used
|
13
|
-
as "archetypical" data structures in test cases, demonstration.
|
10
|
+
## Use cases
|
14
11
|
|
15
|
-
|
12
|
+
* Pulling data from a staging database containing vetted data that has
|
13
|
+
been built up by the development team, users, or business analysts to be loaded and used
|
14
|
+
as "archetypical" data structures in test cases or demos.
|
16
15
|
|
17
|
-
|
16
|
+
* Taking snapshots of production data that has triggered app exceptions
|
17
|
+
to be more closely inspected and incorporated into test cases.
|
18
18
|
|
19
|
-
|
19
|
+
## How it works
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
Feed it an array of ActiveRecord objects or ActiveRelation object and
|
22
|
+
it will allow you to:
|
23
|
+
|
24
|
+
* extract data to .yml fixtures
|
25
|
+
* load it into a database or memory
|
26
|
+
* rebuild .yml fixtures from a saved ActiveRelation extraction query.
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### *Extract* fixture set (typically run in a irb console)
|
23
31
|
|
24
32
|
Fe.extract 'Post.includes(:comments, :author).limit(1)', :name => 'first_post_w_comments_and_authors'
|
33
|
+
# or for multi-model extraction something like this:
|
34
|
+
x = '[UserRole.all, Project.includes(:contributors => [:bio])]'
|
35
|
+
Fe.extract(x,:name => :all_permissions_and_all_projects)
|
25
36
|
|
26
|
-
### Load
|
37
|
+
### *Load* fixture set into database (typically run in a "setup" test method )
|
27
38
|
|
28
39
|
Fe.load_db(:first_post_w_comments_and_authors)
|
29
40
|
|
30
|
-
|
41
|
+
If your fixture set is huge, you can avoid loading particular tables with:
|
31
42
|
|
32
|
-
Fe.
|
43
|
+
Fe.load_db(:first_post_w_comments_and_authors, :only => 'posts')
|
44
|
+
|
45
|
+
Or
|
46
|
+
|
47
|
+
Fe.load_db(:first_post_w_comments_and_authors, :except => ['comments'])
|
48
|
+
|
49
|
+
You can also load to a table name different than the source they were extracted from via a Hash or Proc:
|
50
|
+
|
51
|
+
Via Proc: (this will add "a_prefix_" to all target tables)
|
52
|
+
|
53
|
+
Fe.load_db(:first_post_w_comments_and_authors, :map => -> table_name { "a_prefix_#{table_name}" })
|
33
54
|
|
34
|
-
|
55
|
+
Via Hash: (just maps posts to different table, the others stay the same)
|
56
|
+
|
57
|
+
Fe.load_db(:first_post_w_comments_and_authors, :map => {'posts' => 'different_posts'})
|
58
|
+
|
59
|
+
### *Load particular fixture into memory* (typically used to instantiate an object or build a factory)
|
60
|
+
|
61
|
+
# 'r1' is the fixture's name, all fixture names start with 'r', 1 is the id
|
35
62
|
Fe.get_hash(:first_post_w_comments_and_authors, Post, 'r1')
|
63
|
+
|
64
|
+
# You can specify :first, or :last to the last arg
|
65
|
+
Fe.get_hash(:first_post_w_comments_and_authors, Comment, :first)
|
66
|
+
|
67
|
+
# Get the hash representation of the whole fixture file
|
68
|
+
Fe.get_hash(:first_post_w_comments_and_authors, Comment, :all)
|
69
|
+
|
70
|
+
# Get an array of hashes stored in a fixture file
|
71
|
+
Fe.get_hashes(:first_post_w_comments_and_authors, Comment)
|
72
|
+
|
73
|
+
This feature is used to instantiate objects from the hash or define factories like:
|
74
|
+
|
75
|
+
# Create factory from a particular hash within a fixture file
|
76
|
+
Factory.create(:the_post) do
|
77
|
+
h=Fe.get_hash(:first_post_w_comments_and_authors, Post, :first)
|
78
|
+
name h.name
|
79
|
+
end
|
80
|
+
|
36
81
|
or
|
37
|
-
|
38
|
-
|
39
|
-
Fe.get_hash(:first_post_w_comments_and_authors, Post, :
|
82
|
+
|
83
|
+
# Create an instance
|
84
|
+
h=Fe.get_hash(:first_post_w_comments_and_authors, Post, :first)
|
85
|
+
ye_old_post=Post.new(h)
|
86
|
+
|
87
|
+
### *Rebuild* fixture files associated with the initial extraction (also doable via rake task in Rails)
|
88
|
+
|
89
|
+
Fe.rebuild(:first_post_w_comments_and_authors)
|
90
|
+
# Make sure to `diff` your test/fe_fixtures dir to see what has changed in .yml files
|
91
|
+
|
92
|
+
### *Truncate tables* associated with a fixture set (if you're not using DatabaseCleaner)
|
93
|
+
|
94
|
+
Fe.truncate_tables_for(:first_post_w_comments_and_authors)
|
40
95
|
|
41
96
|
## Installation
|
97
|
+
|
42
98
|
Add this line to your application's Gemfile:
|
43
99
|
|
44
100
|
gem 'iron_fixture_extractor'
|
@@ -51,47 +107,70 @@ Or install it yourself as:
|
|
51
107
|
|
52
108
|
$ gem install iron_fixture_extractor
|
53
109
|
|
54
|
-
|
55
|
-
|
110
|
+
## Advanced Usage/Changing fe_manifest.yml for fixture set
|
111
|
+
|
112
|
+
* Each extracted fixture set has a fe_manifest.yml file that contains
|
113
|
+
details about:
|
114
|
+
|
115
|
+
* The ActiveRelation/ActiveRecord query to used to instantiate objects
|
116
|
+
to be serialized to .yml fixtures.
|
117
|
+
* The models, table names, and row counts of records in the fixture set
|
118
|
+
|
119
|
+
By modifying the :extract_code: field, you can change the extraction
|
120
|
+
behavior associated with .rebuild. It can be handy if you want to add
|
121
|
+
data to a fixture set.
|
122
|
+
|
123
|
+
## Dirt Simple Shiznit
|
124
|
+
|
125
|
+
The essense of the Fe.extract "algorithm" is:
|
56
126
|
|
57
127
|
for each record given to Fe.extract
|
58
128
|
recursively resolve any association pre-loaded in the .association_cache [ActiveRecord] method
|
59
129
|
add it to a set of records keyed by model name
|
60
130
|
write each set of records as a <TheModel.table_name>.yml fixture
|
61
|
-
write a fe_manifest.yml
|
131
|
+
write a fe_manifest.yml containing original query, row counts, etc
|
132
|
+
|
133
|
+
## Typical Workflow
|
134
|
+
* Data extracted from a dev, staging, or production db is needed
|
135
|
+
* Open `rails console` in the appropriate environment
|
136
|
+
* Monkey with ActiveRecord queries to collect the data set you want to use in your test case.
|
137
|
+
* Represent the ActiveRecord query code as a string, i.e. `x=[User.all,Project.includes(:author).find(22)]'`
|
138
|
+
* Extract the data into fixtures, `Fe.extract(x,:name => :some_fixture_set_name)`
|
139
|
+
* Open up test/fe_fixtures/some_fixture_set_name and poke around the yml files to make sure you've captured what you need. Tweak `extract_name` if you need to and `.rebuild`
|
140
|
+
* In your test case's setup method:
|
141
|
+
|
142
|
+
Fe.load_db(:some_fixture_set_name)
|
143
|
+
...then load a instance var to test against:
|
144
|
+
...in this case 22 is the id of a fixture that has just been loaded
|
145
|
+
@the_project = Project.find(22)
|
146
|
+
or
|
147
|
+
Fe.execute_extract_code(:some_fixture_set_name).first
|
148
|
+
|
149
|
+
* In your test case's teardown method:
|
62
150
|
|
151
|
+
DatabaseCleaner.clean...
|
152
|
+
or
|
153
|
+
Fe.truncate_tables_for(:some_fixture_set_name)
|
154
|
+
|
155
|
+
* In your test case `require 'debugger'; debugger; puts 'x'`...inspect @the_project or whatever does the loaded object and db state have the fixtures you want.
|
156
|
+
* Once things seem to be working-ish in your tests, add the fixtures to source control + test case that uses them.
|
63
157
|
|
64
|
-
##
|
65
|
-
|
66
|
-
|
67
|
-
*
|
68
|
-
make extractor implement a sensible .to_s and .inspect
|
69
|
-
* load_db should error if Rails.env or RAILS_ENV is defined and set to
|
70
|
-
production
|
71
|
-
* An :extract_schema option passed to .extract that uses `rake db:structure:dump` functionality
|
72
|
-
or ActiveRecord::Base.connection.structure_dump to create .sql files containing a "create table" statement
|
73
|
-
for each distinct model class. This would allow you to completely de-couple your test cases + fixtures
|
74
|
-
from the external databases they were extracted from.
|
158
|
+
## Gem Compatibility
|
159
|
+
|
160
|
+
* Works on MRI 1.9.3 and 1.9.2
|
161
|
+
* Does not work on JRuby, 1.8.7
|
75
162
|
|
76
163
|
## Contributing
|
77
|
-
To run test cases:
|
78
164
|
|
79
|
-
|
80
|
-
cd iron_fixture_extractor
|
81
|
-
rake # runs test cases
|
165
|
+
In a nutshell:
|
82
166
|
|
83
|
-
|
84
|
-
|
167
|
+
git clone # get the code
|
168
|
+
cd <the dir>
|
169
|
+
rake # run the tests
|
170
|
+
# make a spec file and hack.
|
85
171
|
|
86
|
-
|
87
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
-
3. Ensure the test cases run
|
89
|
-
4. Copy one of the test cases (like basic_test.rb), rename, rip out the guts, and add some tests + code to the app
|
90
|
-
5. Commit your changes (`git commit -am 'Added some feature'`)
|
91
|
-
6. Push to the branch (`git push origin my-new-feature`)
|
92
|
-
7. Create new Pull Request
|
172
|
+
See spec/README_FOR_DEVELOPERS.md for more details.
|
93
173
|
|
94
|
-
If you have other ideas for this tool, make a Github Issue.
|
95
174
|
|
96
175
|
## Footnotes
|
97
176
|
I used various ideas from the following blog posts, gists, and existing
|
@@ -109,3 +188,6 @@ ruby gems, thanks to the authors of these pages:
|
|
109
188
|
* http://www.dan-manges.com/blog/38
|
110
189
|
* http://www.martinfowler.com/bliki/ObjectMother.html
|
111
190
|
* http://asciicasts.com/episodes/158-factories-not-fixtures
|
191
|
+
|
192
|
+
## Author
|
193
|
+
Joe Goggins
|
data/Rakefile
CHANGED
@@ -2,15 +2,14 @@
|
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
require 'rake/testtask'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
t.verbose = true
|
5
|
+
desc "Run the rspec test sweet"
|
6
|
+
task :test do
|
7
|
+
puts "Running specs in /spec."
|
8
|
+
sh "bundle exec rspec"
|
10
9
|
end
|
11
10
|
task :default => 'test'
|
12
11
|
|
13
12
|
desc "Open an irb session preloaded with this library"
|
14
13
|
task :console do
|
15
|
-
sh "irb -rubygems -I lib -I
|
14
|
+
sh "bundle exec irb -rubygems -I lib -I spec -r spec_helper -r fe"
|
16
15
|
end
|
@@ -16,7 +16,4 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = Fe::VERSION
|
17
17
|
gem.add_runtime_dependency "activerecord", "~> 3.2.1"
|
18
18
|
gem.add_runtime_dependency "activesupport", "~> 3.2.1"
|
19
|
-
gem.add_development_dependency "shoulda", "~> 3.0.1"
|
20
|
-
gem.add_development_dependency "sqlite3"
|
21
|
-
gem.add_development_dependency "factory_girl"
|
22
19
|
end
|
data/lib/fe.rb
CHANGED
@@ -3,7 +3,6 @@ module Fe
|
|
3
3
|
class InvalidSourceModelToMapFrom < Exception; end
|
4
4
|
extend ActiveSupport::Autoload
|
5
5
|
autoload :Extractor
|
6
|
-
autoload :FactoryGirlDslMethods
|
7
6
|
require 'fe/railtie' if defined?(Rails)
|
8
7
|
|
9
8
|
# global configuration
|
@@ -28,20 +27,13 @@ module Fe
|
|
28
27
|
|
29
28
|
# Insert fixtures into tables from the yml files in the
|
30
29
|
# "extract_name" fixture set
|
31
|
-
# If you specify a map_models hash here for the last arg like
|
32
|
-
# Post => DifferentPost
|
33
|
-
# It will load the fixtures into a different table than whats listed
|
34
|
-
# in the manifest
|
35
30
|
# NOTE: This is destructive, it will delete everything in the target table
|
36
31
|
#
|
37
|
-
def load_db(extract_name,
|
32
|
+
def load_db(extract_name, options={})
|
38
33
|
extractor = Fe::Extractor.new
|
39
34
|
extractor.name = extract_name
|
40
35
|
extractor.load_from_manifest
|
41
|
-
|
42
|
-
extractor.map_models_hash = map_models
|
43
|
-
end
|
44
|
-
extractor.load_into_database
|
36
|
+
extractor.load_into_database(options)
|
45
37
|
extractor
|
46
38
|
end
|
47
39
|
|
@@ -59,19 +51,13 @@ module Fe
|
|
59
51
|
# Used if you want to get a hash representation of a particular
|
60
52
|
# fixture in a fixture set for a given model
|
61
53
|
#
|
62
|
-
# Used like
|
63
|
-
# h = Fe.get_hash(:first_post_w_comments_and_authors, Post, 'r1')
|
64
|
-
# => {:id => 1, :name => 'first post', ....}
|
65
|
-
# in the console
|
66
|
-
# or in a factory declaration like this
|
54
|
+
# Used like:
|
67
55
|
#
|
68
|
-
#
|
69
|
-
# factory :fe4,
|
70
|
-
# :class => Post,
|
71
|
-
# &Fe.get_hash(:first_post_w_comments_and_authors,Post,"r1").to_factory_girl_string.to_proc
|
72
|
-
# end
|
56
|
+
# h = Fe.get_hash(:first_post_w_comments_and_authors, Post, :first)
|
73
57
|
#
|
74
|
-
#
|
58
|
+
# # => {:id => 1, :name => 'first post', ....}
|
59
|
+
#
|
60
|
+
# in the console
|
75
61
|
#
|
76
62
|
def get_hash(extract_name, model_name, fixture_name)
|
77
63
|
model_name = model_name.to_s
|
@@ -93,34 +79,27 @@ module Fe
|
|
93
79
|
a_hash = h.to_a.first.last
|
94
80
|
when :last
|
95
81
|
a_hash = h.to_a.last.last
|
82
|
+
when :all
|
83
|
+
a_hash = h
|
96
84
|
else
|
97
|
-
raise "symbols can be :first or :
|
85
|
+
raise "symbols can be :first, :last, or :all"
|
98
86
|
end
|
99
87
|
elsif fixture_name.kind_of? String
|
100
88
|
raise "Fixture of the name #{fixture_name} did not exist in in #{fixture_path_for_model}" unless h.has_key?(fixture_name)
|
101
89
|
a_hash = h[fixture_name]
|
102
90
|
else
|
103
|
-
raise "fixture name must be a string or a symbol
|
104
|
-
end
|
105
|
-
a_hash.define_singleton_method(:to_factory_girl_string) do
|
106
|
-
s=<<-EOS
|
107
|
-
x = #{model_name}.new(Fe.get_hash(:#{extract_name},#{model_name},"#{fixture_name}"))
|
108
|
-
EOS
|
109
|
-
model_name.constantize.column_names.each do |col|
|
110
|
-
s << "#{col} x.#{col}\n"
|
111
|
-
end
|
112
|
-
s.instance_eval do
|
113
|
-
def to_proc
|
114
|
-
Proc.new {
|
115
|
-
self
|
116
|
-
}
|
117
|
-
end
|
118
|
-
end
|
119
|
-
s
|
91
|
+
raise "fixture name must be a string or a symbol"
|
120
92
|
end
|
121
93
|
a_hash
|
122
94
|
end
|
123
95
|
|
96
|
+
|
97
|
+
# Syntactic sugar for get_hash(extract_name, model_name, :all).values
|
98
|
+
#
|
99
|
+
def get_hashes(extract_name, model_name)
|
100
|
+
self.get_hash(extract_name, model_name, :all).values
|
101
|
+
end
|
102
|
+
|
124
103
|
# Execute the ActiveRecord query associated with the extract set
|
125
104
|
#
|
126
105
|
def execute_extract_code(extract_name)
|
@@ -150,10 +129,6 @@ module Fe
|
|
150
129
|
end
|
151
130
|
true
|
152
131
|
end
|
153
|
-
|
154
|
-
def augment_factory_girl!
|
155
|
-
FactoryGirl::Syntax::Default::DSL.send(:include, Fe::FactoryGirlDslMethods)
|
156
|
-
end
|
157
132
|
end
|
158
133
|
end
|
159
134
|
|
data/lib/fe/extractor.rb
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
module Fe
|
2
2
|
class Extractor
|
3
|
-
attr_accessor :input_array,
|
4
|
-
|
3
|
+
attr_accessor :input_array,
|
4
|
+
:extract_code,
|
5
|
+
:name,
|
6
|
+
:row_counts,
|
7
|
+
:table_names,
|
8
|
+
:table_name_to_model_name_hash,
|
9
|
+
:manifest_hash
|
10
|
+
|
11
|
+
##################
|
12
|
+
# PUBLIC API #
|
13
|
+
##################
|
14
|
+
#
|
5
15
|
def extract
|
6
16
|
load_input_array_by_executing_extract_code
|
7
17
|
@row_counts = {}
|
@@ -19,7 +29,8 @@ module Fe
|
|
19
29
|
:name => self.name,
|
20
30
|
:model_names => self.model_names,
|
21
31
|
:row_counts => self.row_counts,
|
22
|
-
:table_names => self.models.map {|m| m.table_name}
|
32
|
+
:table_names => self.models.map {|m| m.table_name},
|
33
|
+
:table_name_to_model_name_hash => self.models.inject({}) {|h,m| h[m.table_name] = m.to_s; h }
|
23
34
|
}
|
24
35
|
File.open(self.manifest_file_path,'w') do |file|
|
25
36
|
file.write(@manifest_hash.to_yaml)
|
@@ -27,68 +38,56 @@ module Fe
|
|
27
38
|
self.write_model_fixtures
|
28
39
|
end
|
29
40
|
|
30
|
-
# This is called from 2 types of invocations
|
31
|
-
# Fe.extract('Post.all', :name => :bla)
|
32
|
-
# or
|
33
|
-
# Fe.extract('[Post.all,Comment.all]', :name => :bla2)
|
34
|
-
#
|
35
|
-
def load_from_args(active_relation_or_array,*args)
|
36
|
-
options = args.extract_options!
|
37
|
-
@name = (options[:name] || Time.now.strftime("%Y_%m_%d_%H_%M_%S")).to_sym
|
38
|
-
if active_relation_or_array.kind_of? String
|
39
|
-
@extract_code = active_relation_or_array
|
40
|
-
else
|
41
|
-
raise "Extract code must be a string, so .rebuild can be called"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def load_input_array_by_executing_extract_code
|
46
|
-
@input_array = Array(eval(@extract_code)).to_a
|
47
|
-
end
|
48
|
-
|
49
|
-
def load_from_manifest
|
50
|
-
raise "u gotta set .name to use this method" if self.name.blank?
|
51
|
-
@manifest_hash = YAML.load_file(self.manifest_file_path)
|
52
|
-
@extract_code = @manifest_hash[:extract_code]
|
53
|
-
@name = @manifest_hash[:name]
|
54
|
-
@models = @manifest_hash[:model_names].map {|x| x.constantize}
|
55
|
-
end
|
56
|
-
|
57
|
-
def map_models_hash=(map_models_hash)
|
58
|
-
unless (map_models_hash.keys - self.models).empty?
|
59
|
-
raise InvalidSourceModelToMapFrom.new "your map models hash must contain keys representing class names that exist in the fe_manifest.yml"
|
60
|
-
end
|
61
|
-
@map_models_hash = map_models_hash
|
62
|
-
end
|
63
|
-
|
64
|
-
def map_models_hash
|
65
|
-
if @map_models_hash.nil?
|
66
|
-
{}
|
67
|
-
else
|
68
|
-
@map_models_hash
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
41
|
# Loads data from each fixture file in the extract set using
|
74
42
|
# ActiveRecord::Fixtures
|
75
43
|
#
|
76
|
-
def load_into_database
|
44
|
+
def load_into_database(options={})
|
77
45
|
# necessary to make multiple invocations possible in a single test
|
78
46
|
# case possible
|
79
47
|
ActiveRecord::Fixtures.reset_cache
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
48
|
+
|
49
|
+
# Filter down the models to load if specified
|
50
|
+
the_tables = if options.has_key?(:only)
|
51
|
+
self.table_names.select {|x| Array(options[:only]).include?(x) }
|
52
|
+
elsif options.has_key?(:except)
|
53
|
+
self.table_names.select {|x| !Array(options[:except]).include?(x) }
|
54
|
+
else
|
55
|
+
self.table_names
|
56
|
+
end
|
57
|
+
raise "No models to load, relax your :only or :except filters (or don't bother calling this method)" if the_tables.empty?
|
58
|
+
|
59
|
+
the_tables.each do |table_name|
|
60
|
+
if options[:map].nil?
|
61
|
+
# Vanilla create_fixtures will work fine when no mapping is being used
|
62
|
+
ActiveRecord::Fixtures.create_fixtures(self.target_path, table_name)
|
63
|
+
next
|
85
64
|
else
|
86
|
-
|
65
|
+
# Map table_name via a function (great for prefixing)
|
66
|
+
new_table_name = if options[:map].kind_of?(Proc)
|
67
|
+
options[:map].call(table_name)
|
68
|
+
# Map table_name via a Hash table name mapping
|
69
|
+
elsif options[:map][table_name].kind_of? String
|
70
|
+
options[:map][table_name]
|
71
|
+
else
|
72
|
+
table_name # No mapping for this table name
|
73
|
+
end
|
74
|
+
class_name = self.table_name_to_model_name_hash[table_name]
|
75
|
+
fixtures = ActiveRecord::Fixtures.new( ActiveRecord::Base.connection,
|
76
|
+
new_table_name,
|
77
|
+
class_name,
|
78
|
+
::File.join(self.target_path, table_name))
|
79
|
+
fixtures.table_rows.each do |the_table_name,rows|
|
80
|
+
rows.each do |row|
|
81
|
+
ActiveRecord::Base.connection.insert_fixture(row, the_table_name)
|
82
|
+
end
|
83
|
+
end
|
87
84
|
end
|
85
|
+
# FIXME: The right way to do this is to fork the oracle enhanced adapter
|
86
|
+
# and implement a reset_pk_sequence! method, this is what ActiveRecord::Fixtures
|
87
|
+
# calls. aka this code should be eliminated/live elsewhere.
|
88
88
|
case ActiveRecord::Base.connection.adapter_name
|
89
89
|
when /oracle/i
|
90
90
|
if model.column_names.include? "id"
|
91
|
-
count = model.count
|
92
91
|
sequence_name = model.sequence_name.to_s
|
93
92
|
max_id = model.maximum(:id)
|
94
93
|
next_id = max_id.nil? ? 1 : max_id.to_i + 1
|
@@ -109,6 +108,7 @@ module Fe
|
|
109
108
|
end
|
110
109
|
end
|
111
110
|
end
|
111
|
+
|
112
112
|
# Returns a hash with model class names for keys and Set's of AR
|
113
113
|
# instances for values
|
114
114
|
# aka like this
|
@@ -165,21 +165,49 @@ module Fe
|
|
165
165
|
end
|
166
166
|
@fixture_hashes[model_name]
|
167
167
|
end
|
168
|
+
|
169
|
+
|
170
|
+
#############################
|
171
|
+
# OVERLOADED CONSTRUCTORS #
|
172
|
+
#############################
|
173
|
+
#
|
174
|
+
# * These are used by the Fe module to setup the Extractor object
|
175
|
+
# This is called from 2 types of invocations
|
176
|
+
# Fe.extract('Post.all', :name => :bla)
|
177
|
+
# or
|
178
|
+
# Fe.extract('[Post.all,Comment.all]', :name => :bla2)
|
179
|
+
#
|
180
|
+
def load_from_args(active_relation_or_array,*args)
|
181
|
+
options = args.extract_options!
|
182
|
+
@name = (options[:name] || Time.now.strftime("%Y_%m_%d_%H_%M_%S")).to_sym
|
183
|
+
if active_relation_or_array.kind_of? String
|
184
|
+
@extract_code = active_relation_or_array
|
185
|
+
else
|
186
|
+
raise "Extract code must be a string, so .rebuild can be called"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def load_input_array_by_executing_extract_code
|
191
|
+
@input_array = Array(eval(@extract_code)).to_a
|
192
|
+
end
|
193
|
+
|
194
|
+
def load_from_manifest
|
195
|
+
raise "u gotta set .name to use this method" if self.name.blank?
|
196
|
+
@manifest_hash = YAML.load_file(self.manifest_file_path)
|
197
|
+
@extract_code = @manifest_hash[:extract_code]
|
198
|
+
@name = @manifest_hash[:name]
|
199
|
+
@models = @manifest_hash[:model_names].map {|x| x.constantize}
|
200
|
+
@row_counts = @manifest_hash[:row_counts]
|
201
|
+
@table_names = @manifest_hash[:table_names]
|
202
|
+
@table_name_to_model_name_hash = @manifest_hash[:table_name_to_model_name_hash]
|
203
|
+
end
|
204
|
+
|
168
205
|
protected
|
169
206
|
|
170
207
|
# Recursively goes over all association_cache's from the record and builds the output_hash
|
171
208
|
# This is the meat-and-potatoes of this tool (plus the the recurse
|
172
209
|
# method) is where something interesting is happening
|
173
210
|
#
|
174
|
-
# TODO: To work with ActiveRecord 2.3.x, we'll need to recurse using this logic: (from activerecord-2.3.14/lib/active_record/associations.rb)
|
175
|
-
# # Clears out the association cache
|
176
|
-
# def clear_association_cache #:nodoc:
|
177
|
-
# self.class.reflect_on_all_associations.to_a.each do |assoc|
|
178
|
-
# instance_variable_set "@#{assoc.name}", nil
|
179
|
-
# end unless self.new_record?
|
180
|
-
# end
|
181
|
-
#
|
182
|
-
#
|
183
211
|
def recurse(record)
|
184
212
|
raise "This gem only knows how to extract stuff w ActiveRecord" unless record.kind_of? ActiveRecord::Base
|
185
213
|
key = record.class.base_class.to_s # the base_class is key for correctly handling STI
|