dumpable 0.1.2 → 0.2.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.rdoc +0 -0
- data/Gemfile +1 -0
- data/README.rdoc +47 -12
- data/VERSION +1 -1
- data/dumpable.gemspec +10 -4
- data/lib/dumpable/active_record_extensions.rb +22 -0
- data/lib/dumpable/dumper.rb +96 -0
- data/lib/dumpable/file_writer.rb +16 -0
- data/lib/dumpable.rb +10 -81
- metadata +23 -4
- data/test/test_dumpable.rb +0 -7
data/CHANGELOG.rdoc
ADDED
File without changes
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,19 +1,54 @@
|
|
1
1
|
= dumpable
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
==
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
Output all the sql commands necessary to generate a database row and its relationships.
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
class User < ActiveRecord::Base
|
7
|
+
has_many :posts
|
8
|
+
has_many :roles
|
9
|
+
|
10
|
+
dumpable [:roles, {:posts => :comments}]
|
11
|
+
end
|
12
|
+
|
13
|
+
class Role < ActiveRecord::Base
|
14
|
+
belongs_to :user
|
15
|
+
end
|
16
|
+
|
17
|
+
class Post < ActiveRecord::Base
|
18
|
+
has_many :comments
|
19
|
+
end
|
20
|
+
|
21
|
+
class Comments < ActiveRecord::Base
|
22
|
+
belongs_to :post
|
23
|
+
end
|
24
|
+
|
25
|
+
Then, from the console, you can do something like:
|
26
|
+
User.first.dump
|
27
|
+
This will spit out all the MySQL insertion code for the user, it's roles, it's posts, and each post's comments
|
28
|
+
|
29
|
+
If you want all to pad the ids generated in the insertion code, this can be done as follows:
|
30
|
+
User.first.dump(:id_padding => 1000)
|
31
|
+
This will ensure that the id of the above user is padded by 1000, so a user id of 1 will generate an insert statement with an id of 1001.
|
32
|
+
|
33
|
+
You may wonder why the id is included as part of the insert statement. This is to ensure that all associations maintain their relations to one another.
|
34
|
+
For example, if TODO
|
35
|
+
|
36
|
+
All ActiveRecord models are dumpable automatically, so you could also call:
|
37
|
+
Post.dump
|
38
|
+
|
39
|
+
You can also dump all the entries in the table by calling:
|
40
|
+
Post.dump
|
41
|
+
# or
|
42
|
+
Post.where(:published => true).dump
|
43
|
+
|
44
|
+
== Gotchas
|
45
|
+
At the moment, Dumpable will not work on any complex relationships. This includes has_many :through, and has_and_belongs_to_many.
|
46
|
+
It also will not work on models that have a different primary key than id or on models with complex keys.
|
47
|
+
|
48
|
+
It is also only setup to work with MySQL databases and is untested with any other databases.
|
49
|
+
>>>>>>> Regenerate gemspec for version 0.2.0
|
14
50
|
|
15
51
|
== Copyright
|
16
52
|
|
17
53
|
Copyright (c) 2013 Andrew Hunter. See LICENSE.txt for
|
18
54
|
further details.
|
19
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/dumpable.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "dumpable"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andrew Hunter"]
|
12
|
-
s.date = "2013-06
|
12
|
+
s.date = "2013-11-06"
|
13
13
|
s.description = "Generate the SQL to insert a single record and all of its dependencies"
|
14
14
|
s.email = "andrew.hunter@livingsocial.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
".document",
|
21
21
|
".ruby-gemset",
|
22
22
|
".ruby-version",
|
23
|
+
"CHANGELOG.rdoc",
|
23
24
|
"Gemfile",
|
24
25
|
"LICENSE.txt",
|
25
26
|
"README.rdoc",
|
@@ -27,8 +28,10 @@ Gem::Specification.new do |s|
|
|
27
28
|
"VERSION",
|
28
29
|
"dumpable.gemspec",
|
29
30
|
"lib/dumpable.rb",
|
30
|
-
"
|
31
|
-
"
|
31
|
+
"lib/dumpable/active_record_extensions.rb",
|
32
|
+
"lib/dumpable/dumper.rb",
|
33
|
+
"lib/dumpable/file_writer.rb",
|
34
|
+
"test/helper.rb"
|
32
35
|
]
|
33
36
|
s.homepage = "http://github.com/hunterae/dumpable"
|
34
37
|
s.licenses = ["MIT"]
|
@@ -41,12 +44,14 @@ Gem::Specification.new do |s|
|
|
41
44
|
|
42
45
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
43
46
|
s.add_runtime_dependency(%q<rails>, [">= 0"])
|
47
|
+
s.add_runtime_dependency(%q<hashie>, [">= 0"])
|
44
48
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
45
49
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
46
50
|
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
47
51
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
48
52
|
else
|
49
53
|
s.add_dependency(%q<rails>, [">= 0"])
|
54
|
+
s.add_dependency(%q<hashie>, [">= 0"])
|
50
55
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
51
56
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
52
57
|
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
@@ -54,6 +59,7 @@ Gem::Specification.new do |s|
|
|
54
59
|
end
|
55
60
|
else
|
56
61
|
s.add_dependency(%q<rails>, [">= 0"])
|
62
|
+
s.add_dependency(%q<hashie>, [">= 0"])
|
57
63
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
58
64
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
59
65
|
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Dumpable
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def dump(options={})
|
6
|
+
Dumpable::Dumper.dump(self, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def dumpable(options={})
|
11
|
+
class_eval do
|
12
|
+
cattr_accessor :dumpable_options
|
13
|
+
end
|
14
|
+
self.dumpable_options = Dumpable.config.merge(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def dump(options={})
|
18
|
+
Dumpable::Dumper.dump(self, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Dumpable
|
2
|
+
class Dumper
|
3
|
+
attr_accessor :dumpee, :options, :id_padding, :dumps
|
4
|
+
|
5
|
+
def initialize(dumpee, options)
|
6
|
+
@dumpee = dumpee
|
7
|
+
@options = options
|
8
|
+
@id_padding = @options[:id_padding] || (@dumpee.class.respond_to?(:dumpable_options) && @dumpee.class.dumpable_options[:id_padding]) || Dumpable.config.id_padding
|
9
|
+
@dumps = @options[:dumps] || (@dumpee.class.respond_to?(:dumpable_options) && @dumpee.class.dumpable_options[:dumps])
|
10
|
+
@lines = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def dump
|
14
|
+
recursive_dump(@dumpee, @dumps)
|
15
|
+
@lines << generate_insert_query(@dumpee)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dump(*records_and_collections)
|
19
|
+
options = records_and_collections.extract_options!
|
20
|
+
lines = []
|
21
|
+
records_and_collections.each do |record_or_collection|
|
22
|
+
if record_or_collection.is_a?(Array) || record_or_collection.is_a?(ActiveRecord::Relation) || (record_or_collection.is_a?(Class) && record_or_collection.ancestors.include?(ActiveRecord::Base))
|
23
|
+
record_or_collection = record_or_collection.all if record_or_collection.is_a?(Class) && record_or_collection.ancestors.include?(ActiveRecord::Base)
|
24
|
+
record_or_collection.each do |object|
|
25
|
+
lines << new(object, options).dump
|
26
|
+
end
|
27
|
+
else
|
28
|
+
lines << new(record_or_collection, options).dump
|
29
|
+
end
|
30
|
+
end
|
31
|
+
Dumpable::FileWriter.write(lines.flatten.compact, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def recursive_dump(object, dumps)
|
36
|
+
if dumps.nil?
|
37
|
+
|
38
|
+
elsif dumps.is_a?(Array)
|
39
|
+
dumps.each do |mini_dump|
|
40
|
+
recursive_dump(object, mini_dump)
|
41
|
+
end
|
42
|
+
elsif dumps.is_a?(Hash)
|
43
|
+
dumps.each do |key, value|
|
44
|
+
recursive_dump(object, key)
|
45
|
+
Array(object.send(key)).each { |child| recursive_dump(child, value) }
|
46
|
+
end
|
47
|
+
elsif dumps.is_a?(Symbol) || dumps.is_a?(String)
|
48
|
+
Array(object.send(dumps)).each do |child_object|
|
49
|
+
reflection = object.class.reflections[dumps]
|
50
|
+
if reflection.macro == :belongs_to
|
51
|
+
object.send("#{reflection.association_foreign_key}=", object.id + @id_padding)
|
52
|
+
elsif [:has_many, :has_one].include? reflection.macro
|
53
|
+
if reflection.respond_to?(:foreign_key)
|
54
|
+
child_object.send("#{reflection.foreign_key}=", object.id + @id_padding)
|
55
|
+
else
|
56
|
+
child_object.send("#{reflection.primary_key_name}=", object.id + @id_padding)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@lines << generate_insert_query(child_object)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# http://invisipunk.blogspot.com/2008/04/activerecord-raw-insertupdate.html
|
65
|
+
def generate_insert_query(object)
|
66
|
+
cloned_attributes = object.attributes.clone
|
67
|
+
return nil unless cloned_attributes["id"].present?
|
68
|
+
cloned_attributes["id"] += @id_padding
|
69
|
+
key_values = cloned_attributes.collect{ |key,value| [key, dump_value_string(value)] }
|
70
|
+
keys = key_values.collect{ |item| item[0].to_s }.join(", ")
|
71
|
+
values = key_values.collect{ |item| item[1].to_s }.join(", ")
|
72
|
+
|
73
|
+
"INSERT INTO #{object.class.table_name} (#{ keys }) VALUES (#{ values });"
|
74
|
+
end
|
75
|
+
|
76
|
+
# http://invisipunk.blogspot.com/2008/04/activerecord-raw-insertupdate.html
|
77
|
+
def dump_value_string(value)
|
78
|
+
case value.class.to_s
|
79
|
+
when "Time"
|
80
|
+
"'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
81
|
+
when "NilClass"
|
82
|
+
"NULL"
|
83
|
+
when "Fixnum"
|
84
|
+
value
|
85
|
+
when "String"
|
86
|
+
"'#{value.gsub(/'/, "\\\\'")}'"
|
87
|
+
when "FalseClass"
|
88
|
+
'0'
|
89
|
+
when "TrueClass"
|
90
|
+
'1'
|
91
|
+
else
|
92
|
+
"'#{value}'"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/dumpable.rb
CHANGED
@@ -1,90 +1,19 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_record'
|
3
|
+
require 'hashie'
|
3
4
|
|
4
5
|
module Dumpable
|
5
|
-
|
6
|
+
autoload :ActiveRecordExtensions, "dumpable/active_record_extensions"
|
7
|
+
autoload :Dumper, "dumpable/dumper"
|
8
|
+
autoload :FileWriter, "dumpable/file_writer"
|
6
9
|
|
7
|
-
mattr_accessor :
|
8
|
-
@@
|
10
|
+
mattr_accessor :config
|
11
|
+
@@config = Hashie::Mash.new
|
12
|
+
@@config.id_padding = 0
|
9
13
|
|
10
|
-
def dump(
|
11
|
-
|
12
|
-
|
13
|
-
dumps = options[:dumps] || self.class.dumps
|
14
|
-
|
15
|
-
recursive_dump(self, id_padding, dumps)
|
16
|
-
puts generate_insert_query(id_padding)
|
17
|
-
end
|
18
|
-
|
19
|
-
# http://invisipunk.blogspot.com/2008/04/activerecord-raw-insertupdate.html
|
20
|
-
def generate_insert_query(id_padding)
|
21
|
-
cloned_attributes = self.attributes.clone
|
22
|
-
cloned_attributes["id"] += id_padding
|
23
|
-
key_vals = cloned_attributes.collect{ |key,value| [key, dump_value_string(value)] }
|
24
|
-
"INSERT INTO #{self.class.table_name} " +
|
25
|
-
"( #{key_vals.collect{ |item| item[0].to_s }.join(", ") } ) " +
|
26
|
-
"VALUES( #{key_vals.collect{ |item| item[1].to_s }.join(", ") } );"
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
def recursive_dump(object, id_padding, dumps)
|
31
|
-
if dumps.nil?
|
32
|
-
|
33
|
-
elsif dumps.is_a?(Array)
|
34
|
-
dumps.each do |mini_dump|
|
35
|
-
recursive_dump(object, id_padding, mini_dump)
|
36
|
-
end
|
37
|
-
elsif dumps.is_a?(Hash)
|
38
|
-
dumps.each do |key, value|
|
39
|
-
recursive_dump(object, id_padding, key)
|
40
|
-
Array(object.send(key)).each { |child| recursive_dump(child, id_padding, value) }
|
41
|
-
end
|
42
|
-
elsif dumps.is_a?(Symbol) || dumps.is_a?(String)
|
43
|
-
Array(object.send(dumps)).each do |child_object|
|
44
|
-
reflection = object.class.reflections[dumps]
|
45
|
-
if reflection.macro == :belongs_to
|
46
|
-
object.send("#{reflection.association_foreign_key}=", object.id + id_padding)
|
47
|
-
elsif [:has_many, :has_one].include? reflection.macro
|
48
|
-
if reflection.respond_to?(:foreign_key)
|
49
|
-
child_object.send("#{reflection.foreign_key}=", object.id + id_padding)
|
50
|
-
else
|
51
|
-
child_object.send("#{reflection.primary_key_name}=", object.id + id_padding)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
puts child_object.send(:generate_insert_query, id_padding)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# http://invisipunk.blogspot.com/2008/04/activerecord-raw-insertupdate.html
|
60
|
-
def dump_value_string(value)
|
61
|
-
case value.class.to_s
|
62
|
-
when "Time"
|
63
|
-
"'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
64
|
-
when "NilClass"
|
65
|
-
"NULL"
|
66
|
-
when "Fixnum"
|
67
|
-
value
|
68
|
-
when "String"
|
69
|
-
"'#{value.gsub(/'/, "\\\\'")}'"
|
70
|
-
when "FalseClass"
|
71
|
-
'0'
|
72
|
-
when "TrueClass"
|
73
|
-
'1'
|
74
|
-
else
|
75
|
-
"'#{value}'"
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
module ClassMethods
|
80
|
-
def dumpable(options={})
|
81
|
-
self.class_eval do
|
82
|
-
cattr_accessor :dumpable_id_padding, :dumps
|
83
|
-
end
|
84
|
-
self.dumpable_id_padding = options[:id_padding] || Dumpable.dumpable_id_padding
|
85
|
-
self.dumps = options[:dumps]
|
86
|
-
end
|
14
|
+
def self.dump(*records_and_collections)
|
15
|
+
Dumpable::Dumper.dump(*records_and_collections)
|
87
16
|
end
|
88
17
|
end
|
89
18
|
|
90
|
-
ActiveRecord::Base.send :include, Dumpable
|
19
|
+
ActiveRecord::Base.send :include, Dumpable::ActiveRecordExtensions
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dumpable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06
|
12
|
+
date: 2013-11-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hashie
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: shoulda
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,6 +118,7 @@ files:
|
|
102
118
|
- .document
|
103
119
|
- .ruby-gemset
|
104
120
|
- .ruby-version
|
121
|
+
- CHANGELOG.rdoc
|
105
122
|
- Gemfile
|
106
123
|
- LICENSE.txt
|
107
124
|
- README.rdoc
|
@@ -109,8 +126,10 @@ files:
|
|
109
126
|
- VERSION
|
110
127
|
- dumpable.gemspec
|
111
128
|
- lib/dumpable.rb
|
129
|
+
- lib/dumpable/active_record_extensions.rb
|
130
|
+
- lib/dumpable/dumper.rb
|
131
|
+
- lib/dumpable/file_writer.rb
|
112
132
|
- test/helper.rb
|
113
|
-
- test/test_dumpable.rb
|
114
133
|
homepage: http://github.com/hunterae/dumpable
|
115
134
|
licenses:
|
116
135
|
- MIT
|
@@ -126,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
145
|
version: '0'
|
127
146
|
segments:
|
128
147
|
- 0
|
129
|
-
hash: -
|
148
|
+
hash: -158942502906389966
|
130
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
150
|
none: false
|
132
151
|
requirements:
|