iron_fixture_extractor 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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/lib/fe/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fe
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,26 @@
1
+ About Contributing to this Tool
2
+ ===============================
3
+
4
+ All current and future work in managed via [GitHub issues and milestones](https://github.com/joegoggins/iron_fixture_extractor/issues?state=open). Take a look there first.
5
+
6
+ Running Tests
7
+ -------------
8
+
9
+ ### Run the whole test suite:
10
+
11
+ rake
12
+
13
+ ### Run an individual spec
14
+
15
+ bundle exec spec/<A_SPEC_FILE>.rb
16
+
17
+ Notes
18
+ -----
19
+
20
+ * Uses sqlite to do testing, if you run into a bug that only happens in
21
+ Oracle, MySQL, etc, you could try forking it, submoduling the gem into you project,
22
+ and hacking on it within your project. Feel free to submit an
23
+ issue/pull request even if there isn't tests to cover your
24
+ changes--we'll find a way to test it.
25
+
26
+ * If you have other ideas for this tool, make a Github Issue.
@@ -0,0 +1,11 @@
1
+ source:
2
+ adapter: sqlite3
3
+ database: spec/tmp/source.sqlite3
4
+ pool: 5
5
+ timeout: 5000
6
+
7
+ target:
8
+ adapter: sqlite3
9
+ database: spec/tmp/target.sqlite3
10
+ pool: 5
11
+ timeout: 5000
@@ -0,0 +1,17 @@
1
+ class CreateSomeData < ActiveRecord::Migration
2
+ def up
3
+ a=Author.create :name => "Joe"
4
+ p1=a.posts.create :name => "First post", :serialized_thing => ComplexThing.new
5
+ p1.comments.create :content => "This is crap"
6
+ p2=a.posts.create :name => "Second post", :serialized_thing => ComplexThing.new
7
+ p2.comments.create :content => "This is great"
8
+
9
+ a2=Author.create :name => "Bill"
10
+ g=Group.create :name => "Group 1"
11
+ g.authors << a2
12
+ g.save
13
+
14
+ User::Jerk.create(:name => "Jimmy the jerk")
15
+ User::Admin.create(:name => "Ron the admin")
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePosts < ActiveRecord::Migration
2
+ def change
3
+ create_table :posts do |t|
4
+ t.belongs_to :author
5
+ t.string :name
6
+ t.string :content
7
+ t.string :serialized_thing
8
+
9
+ t.timestamps
10
+ end
11
+ add_index :posts, :author_id
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ class CreateAuthors < ActiveRecord::Migration
2
+ def change
3
+ create_table :authors do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class CreateDifferentPosts < ActiveRecord::Migration
2
+ # DUPLICATED FROM Posts
3
+ def change
4
+ create_table :different_posts do |t|
5
+ t.belongs_to :author
6
+ t.string :name
7
+ t.string :content
8
+ t.string :serialized_thing
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :different_posts, :author_id
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class CreateComments < ActiveRecord::Migration
2
+ def change
3
+ create_table :comments do |t|
4
+ t.belongs_to :author
5
+ t.belongs_to :post
6
+ t.string :content
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :comments, :author_id
11
+ add_index :comments, :post_id
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateGroupMembers < ActiveRecord::Migration
2
+ def change
3
+ create_table :group_members do |t|
4
+ t.belongs_to :author
5
+ t.belongs_to :group
6
+ t.string :role
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :group_members, :author_id
11
+ add_index :group_members, :group_id
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ class CreateGroups < ActiveRecord::Migration
2
+ def change
3
+ create_table :groups do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.string :type
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class Author < ActiveRecord::Base
2
+ attr_accessible :name
3
+ has_many :posts
4
+ has_many :comments
5
+ end
@@ -0,0 +1,5 @@
1
+ class Comment < ActiveRecord::Base
2
+ belongs_to :author
3
+ belongs_to :post
4
+ attr_accessible :content
5
+ end
@@ -0,0 +1,4 @@
1
+ # This is serialized in the Post.serialized_thing attribute.
2
+ #
3
+ class ComplexThing
4
+ end
@@ -0,0 +1,9 @@
1
+ require_relative './serialized_attribute_encoder'
2
+ class DifferentPost < ActiveRecord::Base
3
+ # This is supposed to be identical to post
4
+ # and used to test fixture loading to a model
5
+ # different from the one it was extracted from
6
+ # so behavior doesn't matter
7
+ attr_accessible :content, :name, :serialized_thing
8
+ serialize :serialized_thing, SerializedAttributeEncoder.new
9
+ end
@@ -0,0 +1,5 @@
1
+ class Group < ActiveRecord::Base
2
+ attr_accessible :name
3
+ has_many :group_members
4
+ has_many :authors, :through => :group_members
5
+ end
@@ -0,0 +1,5 @@
1
+ class GroupMember < ActiveRecord::Base
2
+ belongs_to :author
3
+ belongs_to :group
4
+ attr_accessible :role
5
+ end
@@ -0,0 +1,7 @@
1
+ require_relative './serialized_attribute_encoder'
2
+ class Post < ActiveRecord::Base
3
+ belongs_to :author
4
+ has_many :comments
5
+ attr_accessible :content, :name, :serialized_thing
6
+ serialize :serialized_thing, SerializedAttributeEncoder.new
7
+ end
@@ -0,0 +1,12 @@
1
+ # This encoder just serializes complex attributes with the string
2
+ # representation of their class. A +ComplexThing+ instance is stored in
3
+ # the database as 'ComplexThing', not a marshalled Ruby object.
4
+ #
5
+ class SerializedAttributeEncoder
6
+ def load(value)
7
+ value.constantize.new
8
+ end
9
+ def dump(object)
10
+ object.class.to_s
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ require_relative '../user'
2
+ class User::Admin < User
3
+ end
@@ -0,0 +1,3 @@
1
+ require_relative '../user'
2
+ class User::Jerk < User
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fe.execute_extract_code" do
4
+ include FirstPostWCommentsAndAuthors
5
+ it "provides the ability to execute the same query that built the fixtures" do
6
+ FeTestEnv.instance.connect_to_target
7
+ FeTestEnv.instance.create_tables_in('target')
8
+ Fe.load_db(@extract_name)
9
+ rows = Fe.execute_extract_code(@extract_name)
10
+ expect(rows.length).to eql(@extractor.row_counts['Post'])
11
+ expect(rows.first.association_cache.keys - [:comments,:author]).to be_empty, "Comments and authors should be eager loaded"
12
+ end
13
+
14
+ it "ensures the test env is all good..there are no rows" do
15
+ FeTestEnv.instance.connect_to_target
16
+ FeTestEnv.instance.create_tables_in('target')
17
+ @model_names.each do |mn|
18
+ expect(mn.constantize.count).to eql(0)
19
+ end
20
+ end
21
+ it "has a loaded target database with the same number of rows than is in the source" do
22
+ FeTestEnv.instance.connect_to_target
23
+ FeTestEnv.instance.create_tables_in('target')
24
+ Fe.load_db(@extract_name)
25
+ @extractor.row_counts.each_pair do |m,c|
26
+ expect(m.constantize.count).to eql(c), "number of rows in the target should be the same as the counts from the source given the extract_code"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fe.extract and Fe::Extractor instances" do
4
+ include FirstPostWCommentsAndAuthors
5
+ it "should return an Fe::Extractor instance that exposes the extracted data information" do
6
+ expect(@extractor).to be_a_kind_of(Fe::Extractor)
7
+
8
+ # The stuff passed into the constructor
9
+ expect(@extract_name.to_sym).to eql(@extractor.name), "note that the extractor name is a symbol, even if given a string"
10
+ expect(@extract_code).to eql(@extractor.extract_code), "the code used to pull the data"
11
+
12
+ # The models involved
13
+ expect(@extractor.model_names - @model_names).to be_empty
14
+ expect(@extractor.models).to be_a_kind_of(Array)
15
+ expect(@extractor.models.first.ancestors).to include(ActiveRecord::Base)
16
+
17
+ # The fixture files involved
18
+ expect(@extractor.manifest_hash).to be_a_kind_of(Hash), "The hash representation of what is saved to fe_manifest.yml"
19
+ expect(@extractor.output_hash).to be_a_kind_of(Hash), "The hash representation all of the the extracted data"
20
+ @model_names.each do |mn|
21
+ @extractor.output_hash.keys
22
+ end
23
+ end
24
+ it "should put data into the .yml files" do
25
+ expect(Post.table_name).to eql(@extractor.table_names['Post'])
26
+ expect(File.exists?(File.join(Fe.fixtures_root,'first_post_w_comments_and_authors','fe_manifest.yml'))).to eql(true)
27
+ @model_names.each do |mn|
28
+ expect(File.exists?(File.join(Fe.fixtures_root,'first_post_w_comments_and_authors',"#{mn.constantize.table_name}.yml"))).to eql(true)
29
+ end
30
+
31
+ # Asserting the state of the imported data--tied to the data_migrations
32
+ # brittle
33
+ expect(@extractor.row_counts['Post']).to eql(1)
34
+ expect(@extractor.row_counts['Author']).to eql(1)
35
+ end
36
+
37
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fe.extract('[Post.includes(:comments).limit(1),Author.all]', :name => :two_tree) style usage works" do
4
+ it "provides the right output and puts files in the right place" do
5
+ @extract_code = '[Post.includes(:comments).limit(1),Author.all]'
6
+ @extract_name = :two_tree
7
+ extractor = Fe.extract(@extract_code, :name => @extract_name)
8
+ expect(%w(Post Comment Author) - extractor.model_names).to be_empty
9
+ expect(File.exists?(File.join(Fe.fixtures_root,'two_tree','fe_manifest.yml'))).to eql(true)
10
+ expect(extractor.row_counts['Post']).to eql(1)
11
+ end
12
+ end
@@ -0,0 +1,65 @@
1
+ # About
2
+ # =====
3
+ #
4
+ # This tests FeTestEnv, the helper object used to create a dummy environment that the
5
+ # test cases can run. You can almost definitely ignore this unless you suspect the testing
6
+ # framework is broken.
7
+ #
8
+ require 'spec_helper'
9
+
10
+ describe "desired behavior" do
11
+ before(:each) do
12
+ @sqlite_env_path = File.join(File.dirname(__FILE__),'dummy_environments','sqlite','dummy1')
13
+ @fe_test_env = FeTestEnv.new(@sqlite_env_path).initialize_tmp.load_models
14
+ end
15
+
16
+ it "lets us create data structures + data in source and switch to target with no problems",:focus => true do
17
+ @fe_test_env.create_tables_in('source')
18
+ expect(Post.first).to be_nil
19
+ @fe_test_env.create_rows_in('source')
20
+ expect(Post.first).to be_a_kind_of(Post)
21
+ @fe_test_env.connect_to_target
22
+ expect {Post.first}.to raise_exception
23
+ @fe_test_env.create_tables_in('target')
24
+ expect(Post.first).to be_nil
25
+ end
26
+ end
27
+ describe "structure of a dummy environment" do
28
+ before(:each) do
29
+ @sqlite_env_path = File.join(File.dirname(__FILE__),'dummy_environments','sqlite','dummy1')
30
+ @fe_test_env = FeTestEnv.new(@sqlite_env_path).initialize_tmp.load_models
31
+ end
32
+ it "key test env file paths & dirs exist" do
33
+ expect(File.directory?(@fe_test_env.root_path)).to eql(true)
34
+ expect(File.exist?(@fe_test_env.database_dot_yml_file)).to eql(true)
35
+ [:migration_files, :model_files].each do |f|
36
+ expect(@fe_test_env.send(f)).to be_a_kind_of(Array)
37
+ @fe_test_env.send(f).each do |tf|
38
+ expect { load tf }.to_not raise_exception
39
+ end
40
+ end
41
+
42
+ expect(File.directory?(@fe_test_env.tmp_dir)).to eql(true)
43
+ expect(File.directory?(@fe_test_env.fe_fixtures_dir)).to eql(true)
44
+ expect(File.directory?(@fe_test_env.sqlite_db_dir)).to eql(true)
45
+ end
46
+
47
+ it "can connect to source and target" do
48
+ # SOURCE
49
+ @fe_test_env.connect_to_source
50
+ source_db = @fe_test_env.database_dot_yml_hash['source']['database']
51
+ expect(ActiveRecord::Base.connection).to be_a_kind_of(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
52
+ ActiveRecord::Base.connection.execute('create table t (id int)')
53
+ expect(ActiveRecord::Base.connection.tables.length).to eql(1)
54
+ expect(File.exists?(source_db)).to eql(true)
55
+
56
+ # TARGET
57
+ #
58
+ @fe_test_env.connect_to_target
59
+ target_db = @fe_test_env.database_dot_yml_hash['target']['database']
60
+ expect(ActiveRecord::Base.connection.instance_variable_get(:@config)[:database]).to eql(target_db)
61
+ expect(ActiveRecord::Base.connection.tables.length).to eql(0) # there is no table in target
62
+ expect(File.exists?(target_db)).to eql(true)
63
+ end
64
+ end
65
+
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fe.get_hash and .get_hashes" do
4
+ include FirstPostWCommentsAndAuthors
5
+ it "returns a hash with the right columns for different ways of invoking, fixture name, :first, :last" do
6
+ ['r1', # By the fixture hash name (auto-generated by Fe)
7
+ :first, # By the first fixture in the .yml file
8
+ :last # By the last fixture in the yml file
9
+ ].each do |invoke_type|
10
+ h = Fe.get_hash(:first_post_w_comments_and_authors, Post, invoke_type)
11
+ expect(h).to be_a_kind_of(Hash)
12
+ expect(h.keys - Post.column_names).to be_empty
13
+ end
14
+ end
15
+ it "allows you to get the whole yaml file with get_hash(...,:all)" do
16
+ another_extractor = Fe.extract('Post.all', :name => :all_posts)
17
+ all_way_1 = Fe.get_hash(:all_posts, Post, :all)
18
+ expect(all_way_1).to be_a_kind_of(Hash)
19
+ expect(all_way_1.keys).to eql(['r1','r2']), "This hash is essentially the entire .yml file gobbled up as a data-structure"
20
+ end
21
+ it "provides .get_hashes which returns an array (as syntactic sugar for get_hash...:all" do
22
+ another_extractor = Fe.extract('Post.all', :name => :all_posts)
23
+ all_way_1 = Fe.get_hash(:all_posts, Post, :all)
24
+ all_way_2 = Fe.get_hashes(:all_posts, Post)
25
+ expect(all_way_2).to eql(all_way_1.values)
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fe.load_db(..., :map)" do
4
+ before(:each) do
5
+ FeTestEnv.instance.bomb_and_rebuild
6
+ Fe.extract("Post.where('1=1')", :name => :all_posts)
7
+ FeTestEnv.instance.connect_to_target
8
+ FeTestEnv.instance.create_tables_in('target')
9
+ end
10
+
11
+ it "should allow you to load into a different table via Hash mapping" do
12
+ expect {
13
+ Fe.load_db(:all_posts, :map => {'posts' => 'different_posts'})
14
+ }.to change {
15
+ DifferentPost.count
16
+ }.from(0)
17
+ expect(Post.count).to eql(0)
18
+ end
19
+
20
+ it "should allow you to load into a different table via Proc mapping" do
21
+ expect {
22
+ Fe.load_db(:all_posts, :map => -> table_name { "different_#{table_name}" })
23
+ }.to change {
24
+ DifferentPost.count
25
+ }.from(0)
26
+ expect(Post.count).to eql(0)
27
+ end
28
+
29
+ it "should allow you to load into a different table but same model" do
30
+ begin
31
+ Post.connection.execute("ALTER TABLE posts RENAME TO foo_posts")
32
+ Post.table_name = 'foo_posts'
33
+ Fe.load_db(:all_posts, :map => -> table_name { "foo_#{table_name}" })
34
+ expect(Post.count).to be > 0
35
+ ensure
36
+ Post.table_name = 'posts'
37
+ end
38
+ end
39
+ end