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.
Files changed (56) hide show
  1. data/CHANGELOG.md +6 -0
  2. data/Gemfile +7 -9
  3. data/README.md +129 -47
  4. data/Rakefile +5 -6
  5. data/iron_fixture_extractor.gemspec +0 -3
  6. data/lib/fe.rb +18 -43
  7. data/lib/fe/extractor.rb +91 -63
  8. data/lib/fe/version.rb +1 -1
  9. data/spec/README_FOR_DEVELOPERS.md +26 -0
  10. data/spec/dummy_environments/sqlite/dummy1/config/database.yml +11 -0
  11. data/spec/dummy_environments/sqlite/dummy1/data_migrations/001_create_some_data.rb +17 -0
  12. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043601_create_posts.rb +13 -0
  13. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043602_create_authors.rb +9 -0
  14. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043603_create_different_posts.rb +14 -0
  15. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043639_create_comments.rb +13 -0
  16. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043808_create_group_members.rb +13 -0
  17. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043818_create_groups.rb +9 -0
  18. data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043819_create_users.rb +10 -0
  19. data/spec/dummy_environments/sqlite/dummy1/models/author.rb +5 -0
  20. data/spec/dummy_environments/sqlite/dummy1/models/comment.rb +5 -0
  21. data/spec/dummy_environments/sqlite/dummy1/models/complex_thing.rb +4 -0
  22. data/spec/dummy_environments/sqlite/dummy1/models/different_post.rb +9 -0
  23. data/spec/dummy_environments/sqlite/dummy1/models/group.rb +5 -0
  24. data/spec/dummy_environments/sqlite/dummy1/models/group_member.rb +5 -0
  25. data/spec/dummy_environments/sqlite/dummy1/models/post.rb +7 -0
  26. data/spec/dummy_environments/sqlite/dummy1/models/serialized_attribute_encoder.rb +12 -0
  27. data/spec/dummy_environments/sqlite/dummy1/models/user.rb +2 -0
  28. data/spec/dummy_environments/sqlite/dummy1/models/user/admin.rb +3 -0
  29. data/spec/dummy_environments/sqlite/dummy1/models/user/jerk.rb +3 -0
  30. data/spec/execute_extract_code_spec.rb +29 -0
  31. data/spec/extract_spec.rb +37 -0
  32. data/spec/extract_works_with_multiple_extract_queries_in_one_command_spec.rb +12 -0
  33. data/spec/fe_test_env_spec.rb +65 -0
  34. data/spec/get_hash_spec.rb +27 -0
  35. data/spec/load_db_map_option_spec.rb +39 -0
  36. data/spec/load_db_spec.rb +48 -0
  37. data/spec/load_db_works_with_serialized_attributes_spec.rb +18 -0
  38. data/spec/rebuild_spec.rb +18 -0
  39. data/spec/spec_helper.rb +15 -0
  40. data/spec/support/fe_test_env.rb +181 -0
  41. data/spec/support/first_post_w_comments_and_authors.rb +12 -0
  42. data/spec/truncate_tables_for_spec.rb +39 -0
  43. data/spec/works_with_single_table_inheritance_spec.rb +16 -0
  44. data/test/basic_usage_test.ported.rb +114 -0
  45. data/test/{different_target_table_test.rb → different_target_table_test.not_porting.rb} +0 -0
  46. data/test/factory_girl_test.not_porting_now.rb +79 -0
  47. data/test/{fe_test_env.rb → fe_test_env.ported.rb} +0 -0
  48. data/test/{get_hash_test.rb → get_hash_test.ported.rb} +17 -1
  49. data/test/{multi_tree_usage_test.rb → multi_tree_usage_test.ported.rb} +0 -0
  50. data/test/{serialized_attribute_test.rb → serialized_attribute_test.ported.rb} +0 -0
  51. data/test/{sti_test.rb → sti_test.ported.rb} +0 -0
  52. data/test/test_helper.rb +0 -1
  53. metadata +135 -88
  54. data/lib/fe/factory_girl_dsl_methods.rb +0 -28
  55. data/test/basic_usage_test.rb +0 -106
  56. data/test/factory_girl_test.rb +0 -77
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 1.0.0 -> 1.1.0
2
+ * Shouldn't be any breaking changes, mostly a switch to rspec for
3
+ testing.
4
+
5
+ * Added Fe.load_db(...) :only, :except, and :map options
6
+
1
7
  ## 0.1.1 -> 1.0.0
2
8
  * Fe.extract must be given a string or array of ActiveRecord queries
3
9
  like this
data/Gemfile CHANGED
@@ -1,12 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # TEMP CRAP FOR DEV, COMMENT OUT OR DELETE
4
- #
5
- gem 'debugger'
6
-
7
- # USED w ruby 1.9.2
8
- # http://dirk.net/2010/04/17/ruby-debug-with-ruby-19x-and-rails-3-on-rvm/
9
- #gem 'ruby-debug19', :require => 'ruby-debug'
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 (works great with whatever testing framework your using).
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
- Its good for:
7
+ * your data is too complex for factories
8
+ * creating and maintaining manual fixtures is cumbersome and brittle
10
9
 
11
- Pulling data from a staging database containing vetted data that has
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
- Taking snapshots of production data that has triggered unexpected errors to be incorporated into test cases for closer inspection and correction.
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
- How it works:
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
- Feed it an array of ActiveRecord objects or ActiveRelation object and it will allow you to extract, load, and rebuild the records associated with your queries.
19
+ ## How it works
20
20
 
21
- ## Usage (typically in a console)
22
- ### Extract (fixture files)
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 (dataset into database)
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
- ### Rebuild (fixture files associated with the initial extraction)
41
+ If your fixture set is huge, you can avoid loading particular tables with:
31
42
 
32
- Fe.rebuild(:first_post_w_comments_and_authors)
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
- ### Pull up a hash from a particular fixture file (very handy in test cases)
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
- Fe.get_hash(:first_post_w_comments_and_authors, Post, :first)
38
- or
39
- Fe.get_hash(:first_post_w_comments_and_authors, Post, :last)
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
- ### Extract
55
- The essense of the Fe.extract is dirt simple:
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 that will allow you to later change the query, inspect row counts, and rebuild the fixtures by re-executing the originate queries
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
- ## Missing Features
65
- * If you give a non-string arg to .extract, the manifest should resolve
66
- the .extract_code to be a bunch of look-ups by primary key ala [Post.find(1),Comment.find(2)].
67
- * The output of each of the main commands should be meaningful, aka,
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
- # clone or fork repo
80
- cd iron_fixture_extractor
81
- rake # runs test cases
165
+ In a nutshell:
82
166
 
83
- Help on the missing features above would be much appreciated per the
84
- usual github approach:
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
- 1. Fork it
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
- Rake::TestTask.new(:test) do |t|
6
- t.libs << 'lib'
7
- t.libs << 'test'
8
- t.pattern = 'test/**/*_test.rb'
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 test -r test_helper -r fe"
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, map_models={})
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
- unless map_models.empty?
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
- # FactoryGirl.define do
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
- # Kinda meta, but somewhat useful
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 :last"
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 like :first or :laset"
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, :extract_code, :name, :row_counts,:table_names, :manifest_hash
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
- self.models.each do |model|
81
- if self.map_models_hash.has_key?(model)
82
- raise "Unfortunately, this is not implemented...from the implementation of .create_fixtures it seems impossible...keeping this code in case its not"
83
- h = {model.table_name => self.map_models_hash[model]}
84
- ActiveRecord::Fixtures.create_fixtures(self.target_path, model.table_name, h)
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
- ActiveRecord::Fixtures.create_fixtures(self.target_path, model.table_name)
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