Empact-activerecord-import 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.markdown +25 -0
  2. data/Rakefile +72 -0
  3. data/VERSION +1 -0
  4. data/lib/activerecord-import.rb +16 -0
  5. data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +10 -0
  6. data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +6 -0
  7. data/lib/activerecord-import/active_record/adapters/mysql_adapter.rb +6 -0
  8. data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +7 -0
  9. data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +7 -0
  10. data/lib/activerecord-import/adapters/abstract_adapter.rb +125 -0
  11. data/lib/activerecord-import/adapters/mysql_adapter.rb +50 -0
  12. data/lib/activerecord-import/adapters/postgresql_adapter.rb +13 -0
  13. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +7 -0
  14. data/lib/activerecord-import/base.rb +27 -0
  15. data/lib/activerecord-import/import.rb +347 -0
  16. data/lib/activerecord-import/mysql.rb +8 -0
  17. data/lib/activerecord-import/mysql2.rb +8 -0
  18. data/lib/activerecord-import/postgresql.rb +8 -0
  19. data/lib/activerecord-import/sqlite3.rb +8 -0
  20. data/test/active_record/connection_adapter_test.rb +52 -0
  21. data/test/adapters/mysql.rb +1 -0
  22. data/test/adapters/mysql2.rb +1 -0
  23. data/test/adapters/postgresql.rb +1 -0
  24. data/test/adapters/sqlite3.rb +1 -0
  25. data/test/import_test.rb +202 -0
  26. data/test/models/book.rb +3 -0
  27. data/test/models/group.rb +3 -0
  28. data/test/models/topic.rb +7 -0
  29. data/test/mysql/import_test.rb +6 -0
  30. data/test/mysql2/import_test.rb +6 -0
  31. data/test/postgresql/import_test.rb +20 -0
  32. data/test/schema/generic_schema.rb +98 -0
  33. data/test/schema/mysql_schema.rb +17 -0
  34. data/test/schema/version.rb +4 -0
  35. data/test/support/active_support/test_case_extensions.rb +67 -0
  36. data/test/support/factories.rb +13 -0
  37. data/test/support/generate.rb +29 -0
  38. data/test/support/mysql/assertions.rb +55 -0
  39. data/test/support/mysql/import_examples.rb +117 -0
  40. data/test/test_helper.rb +46 -0
  41. metadata +169 -0
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table :books, :options=>'ENGINE=MyISAM', :force=>true do |t|
4
+ t.column :title, :string, :null=>false
5
+ t.column :publisher, :string, :null=>false, :default => 'Default Publisher'
6
+ t.column :author_name, :string, :null=>false
7
+ t.column :created_at, :datetime
8
+ t.column :created_on, :datetime
9
+ t.column :updated_at, :datetime
10
+ t.column :updated_on, :datetime
11
+ t.column :publish_date, :date
12
+ t.column :topic_id, :integer
13
+ t.column :for_sale, :boolean, :default => true
14
+ end
15
+ execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )"
16
+
17
+ end
@@ -0,0 +1,4 @@
1
+ class SchemaInfo < ActiveRecord::Base
2
+ set_table_name 'schema_info'
3
+ VERSION = 12
4
+ end
@@ -0,0 +1,67 @@
1
+ class ActiveSupport::TestCase
2
+ include ActiveRecord::TestFixtures
3
+ self.use_transactional_fixtures = true
4
+
5
+ class << self
6
+ def assertion(name, &block)
7
+ mc = class << self ; self ; end
8
+ mc.class_eval do
9
+ define_method(name) do
10
+ it(name, &block)
11
+ end
12
+ end
13
+ end
14
+
15
+ def asssertion_group(name, &block)
16
+ mc = class << self ; self ; end
17
+ mc.class_eval do
18
+ define_method(name, &block)
19
+ end
20
+ end
21
+
22
+ def macro(name, &block)
23
+ class_eval do
24
+ define_method(name, &block)
25
+ end
26
+ end
27
+
28
+ def describe(description, toplevel=nil, &blk)
29
+ text = toplevel ? description : "#{name} #{description}"
30
+ klass = Class.new(self)
31
+
32
+ klass.class_eval <<-RUBY_EVAL
33
+ def self.name
34
+ "#{text}"
35
+ end
36
+ RUBY_EVAL
37
+
38
+ # do not inherit test methods from the superclass
39
+ klass.class_eval do
40
+ instance_methods.grep(/^test.+/) do |method|
41
+ undef_method method
42
+ end
43
+ end
44
+
45
+ klass.instance_eval &blk
46
+ end
47
+ alias_method :context, :describe
48
+
49
+ def let(name, &blk)
50
+ values = {}
51
+ define_method(name) do
52
+ return values[name] if values.has_key?(name)
53
+ values[name] = instance_eval(&blk)
54
+ end
55
+ end
56
+
57
+ def it(description, &blk)
58
+ define_method("test: #{name} #{description}", &blk)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ def describe(description, &blk)
65
+ ActiveSupport::TestCase.describe(description, true, &blk)
66
+ end
67
+
@@ -0,0 +1,13 @@
1
+ Factory.define :group do |m|
2
+ m.sequence(:order) { |n| "Order #{n}" }
3
+ end
4
+
5
+ Factory.define :invalid_topic, :class => "Topic" do |m|
6
+ m.sequence(:title){ |n| "Title #{n}"}
7
+ m.author_name nil
8
+ end
9
+
10
+ Factory.define :topic do |m|
11
+ m.sequence(:title){ |n| "Title #{n}"}
12
+ m.sequence(:author_name){ |n| "Author #{n}"}
13
+ end
@@ -0,0 +1,29 @@
1
+ class ActiveSupport::TestCase
2
+ def Build(*args)
3
+ n = args.shift if args.first.is_a?(Numeric)
4
+ factory = args.shift
5
+ factory_girl_args = args.shift || {}
6
+
7
+ if n
8
+ Array.new.tap do |collection|
9
+ n.times.each { collection << Factory.build(factory.to_s.singularize.to_sym, factory_girl_args) }
10
+ end
11
+ else
12
+ Factory.build(factory.to_s.singularize.to_sym, factory_girl_args)
13
+ end
14
+ end
15
+
16
+ def Generate(*args)
17
+ n = args.shift if args.first.is_a?(Numeric)
18
+ factory = args.shift
19
+ factory_girl_args = args.shift || {}
20
+
21
+ if n
22
+ Array.new.tap do |collection|
23
+ n.times.each { collection << Factory.create(factory.to_s.singularize.to_sym, factory_girl_args) }
24
+ end
25
+ else
26
+ Factory.create(factory.to_s.singularize.to_sym, factory_girl_args)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ class ActiveSupport::TestCase
2
+ module MySQLAssertions
3
+ def self.extended(klass)
4
+ klass.instance_eval do
5
+ assertion(:should_not_update_created_at_on_timestamp_columns) do
6
+ Delorean.time_travel_to("5 minutes from now") do
7
+ perform_import
8
+ assert_equal @topic.created_at.to_i, updated_topic.created_at.to_i
9
+ assert_equal @topic.created_on.to_i, updated_topic.created_on.to_i
10
+ end
11
+ end
12
+
13
+ assertion(:should_update_updated_at_on_timestamp_columns) do
14
+ time = Chronic.parse("5 minutes from now")
15
+ Delorean.time_travel_to(time) do
16
+ perform_import
17
+ assert_equal time.to_i, updated_topic.updated_at.to_i
18
+ assert_equal time.to_i, updated_topic.updated_on.to_i
19
+ end
20
+ end
21
+
22
+ assertion(:should_not_update_timestamps) do
23
+ Delorean.time_travel_to("5 minutes from now") do
24
+ perform_import :timestamps => false
25
+ assert_equal @topic.created_at.to_i, updated_topic.created_at.to_i
26
+ assert_equal @topic.created_on.to_i, updated_topic.created_on.to_i
27
+ assert_equal @topic.updated_at.to_i, updated_topic.updated_at.to_i
28
+ assert_equal @topic.updated_on.to_i, updated_topic.updated_on.to_i
29
+ end
30
+ end
31
+
32
+ assertion(:should_not_update_fields_not_mentioned) do
33
+ assert_equal "John Doe", updated_topic.author_name
34
+ end
35
+
36
+ assertion(:should_update_fields_mentioned) do
37
+ perform_import
38
+ assert_equal "Book - 2nd Edition", updated_topic.title
39
+ assert_equal "johndoe@example.com", updated_topic.author_email_address
40
+ end
41
+
42
+ assertion(:should_update_fields_mentioned_with_hash_mappings) do
43
+ perform_import
44
+ assert_equal "johndoe@example.com", updated_topic.title
45
+ assert_equal "Book - 2nd Edition", updated_topic.author_email_address
46
+ end
47
+
48
+ assertion(:should_update_foreign_keys) do
49
+ perform_import
50
+ assert_equal 57, updated_topic.parent_id
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,117 @@
1
+ def should_support_mysql_import_functionality
2
+ describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do
3
+ extend ActiveSupport::TestCase::MySQLAssertions
4
+
5
+ asssertion_group(:should_support_on_duplicate_key_update) do
6
+ should_not_update_fields_not_mentioned
7
+ should_update_foreign_keys
8
+ should_not_update_created_at_on_timestamp_columns
9
+ should_update_updated_at_on_timestamp_columns
10
+ end
11
+
12
+ macro(:perform_import){ raise "supply your own #perform_import in a context below" }
13
+ macro(:updated_topic){ Topic.find(@topic) }
14
+
15
+ context "given columns and values with :validation checks turned off" do
16
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
17
+ let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
18
+ let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
19
+
20
+ macro(:perform_import) do |*opts|
21
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
22
+ end
23
+
24
+ setup do
25
+ Topic.import columns, values, :validate => false
26
+ @topic = Topic.find 99
27
+ end
28
+
29
+ context "using string column names" do
30
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
31
+ should_support_on_duplicate_key_update
32
+ should_update_fields_mentioned
33
+ end
34
+
35
+ context "using symbol column names" do
36
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
37
+ should_support_on_duplicate_key_update
38
+ should_update_fields_mentioned
39
+ end
40
+
41
+ context "using string hash map" do
42
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
43
+ should_support_on_duplicate_key_update
44
+ should_update_fields_mentioned
45
+ end
46
+
47
+ context "using string hash map, but specifying column mismatches" do
48
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
49
+ should_support_on_duplicate_key_update
50
+ should_update_fields_mentioned_with_hash_mappings
51
+ end
52
+
53
+ context "using symbol hash map" do
54
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
55
+ should_support_on_duplicate_key_update
56
+ should_update_fields_mentioned
57
+ end
58
+
59
+ context "using symbol hash map, but specifying column mismatches" do
60
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
61
+ should_support_on_duplicate_key_update
62
+ should_update_fields_mentioned_with_hash_mappings
63
+ end
64
+ end
65
+
66
+ context "given array of model instances with :validation checks turned off" do
67
+ macro(:perform_import) do |*opts|
68
+ @topic.title = "Book - 2nd Edition"
69
+ @topic.author_name = "Author Should Not Change"
70
+ @topic.author_email_address = "johndoe@example.com"
71
+ @topic.parent_id = 57
72
+ Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
73
+ end
74
+
75
+ setup do
76
+ @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17)
77
+ end
78
+
79
+ context "using string column names" do
80
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
81
+ should_support_on_duplicate_key_update
82
+ should_update_fields_mentioned
83
+ end
84
+
85
+ context "using symbol column names" do
86
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
87
+ should_support_on_duplicate_key_update
88
+ should_update_fields_mentioned
89
+ end
90
+
91
+ context "using string hash map" do
92
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
93
+ should_support_on_duplicate_key_update
94
+ should_update_fields_mentioned
95
+ end
96
+
97
+ context "using string hash map, but specifying column mismatches" do
98
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
99
+ should_support_on_duplicate_key_update
100
+ should_update_fields_mentioned_with_hash_mappings
101
+ end
102
+
103
+ context "using symbol hash map" do
104
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
105
+ should_support_on_duplicate_key_update
106
+ should_update_fields_mentioned
107
+ end
108
+
109
+ context "using symbol hash map, but specifying column mismatches" do
110
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
111
+ should_support_on_duplicate_key_update
112
+ should_update_fields_mentioned_with_hash_mappings
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,46 @@
1
+ require 'pathname'
2
+ test_dir = Pathname.new File.dirname(__FILE__)
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require "fileutils"
7
+ require "rubygems"
8
+
9
+ ENV["RAILS_ENV"] = "test"
10
+
11
+ require "bundler"
12
+ Bundler.setup
13
+
14
+ require "logger"
15
+ require 'test/unit'
16
+ require "active_record"
17
+ require "active_record/fixtures"
18
+ require "active_support/test_case"
19
+
20
+ require "delorean"
21
+ require "ruby-debug"
22
+
23
+ adapter = ENV["ARE_DB"] || "sqlite3"
24
+
25
+ FileUtils.mkdir_p 'log'
26
+ ActiveRecord::Base.logger = Logger.new("log/test.log")
27
+ ActiveRecord::Base.logger.level = Logger::DEBUG
28
+ ActiveRecord::Base.configurations["test"] = YAML.load(test_dir.join("database.yml").open)[adapter]
29
+
30
+ require "activerecord-import"
31
+ ActiveRecord::Base.establish_connection "test"
32
+
33
+ ActiveSupport::Notifications.subscribe(/active_record.sql/) do |event, _, _, _, hsh|
34
+ ActiveRecord::Base.logger.info hsh[:sql]
35
+ end
36
+
37
+ require "factory_girl"
38
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each{ |file| require file }
39
+
40
+ # Load base/generic schema
41
+ require test_dir.join("schema/version")
42
+ require test_dir.join("schema/generic_schema")
43
+ adapter_schema = test_dir.join("schema/#{adapter}_schema.rb")
44
+ require adapter_schema if File.exists?(adapter_schema)
45
+
46
+ Dir[File.dirname(__FILE__) + "/models/*.rb"].each{ |file| require file }
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Empact-activerecord-import
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - Zach Dennis
13
+ - Ben Woosley
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-17 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
30
+ - 0
31
+ - 0
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 1
45
+ - 4
46
+ - 0
47
+ version: 1.4.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: activerecord
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 3
60
+ - 0
61
+ - 0
62
+ version: 3.0.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: *id003
66
+ description: Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond
67
+ email: ben.woosley@gmail.com
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files:
73
+ - README.markdown
74
+ files:
75
+ - README.markdown
76
+ - Rakefile
77
+ - VERSION
78
+ - lib/activerecord-import.rb
79
+ - lib/activerecord-import/active_record/adapters/abstract_adapter.rb
80
+ - lib/activerecord-import/active_record/adapters/mysql2_adapter.rb
81
+ - lib/activerecord-import/active_record/adapters/mysql_adapter.rb
82
+ - lib/activerecord-import/active_record/adapters/postgresql_adapter.rb
83
+ - lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb
84
+ - lib/activerecord-import/adapters/abstract_adapter.rb
85
+ - lib/activerecord-import/adapters/mysql_adapter.rb
86
+ - lib/activerecord-import/adapters/postgresql_adapter.rb
87
+ - lib/activerecord-import/adapters/sqlite3_adapter.rb
88
+ - lib/activerecord-import/base.rb
89
+ - lib/activerecord-import/import.rb
90
+ - lib/activerecord-import/mysql.rb
91
+ - lib/activerecord-import/mysql2.rb
92
+ - lib/activerecord-import/postgresql.rb
93
+ - lib/activerecord-import/sqlite3.rb
94
+ - test/active_record/connection_adapter_test.rb
95
+ - test/adapters/mysql.rb
96
+ - test/adapters/mysql2.rb
97
+ - test/adapters/postgresql.rb
98
+ - test/adapters/sqlite3.rb
99
+ - test/import_test.rb
100
+ - test/models/book.rb
101
+ - test/models/group.rb
102
+ - test/models/topic.rb
103
+ - test/mysql/import_test.rb
104
+ - test/mysql2/import_test.rb
105
+ - test/postgresql/import_test.rb
106
+ - test/schema/generic_schema.rb
107
+ - test/schema/mysql_schema.rb
108
+ - test/schema/version.rb
109
+ - test/support/active_support/test_case_extensions.rb
110
+ - test/support/factories.rb
111
+ - test/support/generate.rb
112
+ - test/support/mysql/assertions.rb
113
+ - test/support/mysql/import_examples.rb
114
+ - test/test_helper.rb
115
+ has_rdoc: true
116
+ homepage: http://github.com/Empact/activerecord-import
117
+ licenses: []
118
+
119
+ post_install_message:
120
+ rdoc_options: []
121
+
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 1663017745777293064
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ requirements: []
142
+
143
+ rubyforge_project:
144
+ rubygems_version: 1.3.7
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: Bulk-loading extension for ActiveRecord
148
+ test_files:
149
+ - test/active_record/connection_adapter_test.rb
150
+ - test/adapters/mysql.rb
151
+ - test/adapters/mysql2.rb
152
+ - test/adapters/postgresql.rb
153
+ - test/adapters/sqlite3.rb
154
+ - test/import_test.rb
155
+ - test/models/book.rb
156
+ - test/models/group.rb
157
+ - test/models/topic.rb
158
+ - test/mysql/import_test.rb
159
+ - test/mysql2/import_test.rb
160
+ - test/postgresql/import_test.rb
161
+ - test/schema/generic_schema.rb
162
+ - test/schema/mysql_schema.rb
163
+ - test/schema/version.rb
164
+ - test/support/active_support/test_case_extensions.rb
165
+ - test/support/factories.rb
166
+ - test/support/generate.rb
167
+ - test/support/mysql/assertions.rb
168
+ - test/support/mysql/import_examples.rb
169
+ - test/test_helper.rb