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/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