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/lib/fe/version.rb
CHANGED
@@ -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,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
|
data/spec/dummy_environments/sqlite/dummy1/migrations/20120604043603_create_different_posts.rb
ADDED
@@ -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
|
+
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,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,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
|