dumpable 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
File without changes
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gem "rails"
4
+ gem "hashie"
4
5
 
5
6
  group :development do
6
7
  gem "shoulda", ">= 0"
data/README.rdoc CHANGED
@@ -1,19 +1,54 @@
1
1
  = dumpable
2
2
 
3
- Description goes here.
4
-
5
- == Contributing to dumpable
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
- * Fork the project.
10
- * Start a feature/bugfix branch.
11
- * Commit and push until you are happy with your contribution.
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
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.2
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.1.2"
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-21"
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
- "test/helper.rb",
31
- "test/test_dumpable.rb"
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
@@ -0,0 +1,16 @@
1
+ module Dumpable
2
+ class FileWriter
3
+ def self.write(lines, options)
4
+ if options[:file]
5
+ File.open(options[:file], "w") do |file|
6
+ file.puts lines
7
+ end
8
+ else
9
+ lines.each do |line|
10
+ puts line
11
+ end
12
+ end
13
+ nil
14
+ end
15
+ end
16
+ 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
- extend ActiveSupport::Concern
6
+ autoload :ActiveRecordExtensions, "dumpable/active_record_extensions"
7
+ autoload :Dumper, "dumpable/dumper"
8
+ autoload :FileWriter, "dumpable/file_writer"
6
9
 
7
- mattr_accessor :dumpable_id_padding
8
- @@dumpable_id_padding = 0
10
+ mattr_accessor :config
11
+ @@config = Hashie::Mash.new
12
+ @@config.id_padding = 0
9
13
 
10
- def dump(options={})
11
- id_padding = options[:id_padding] || self.class.dumpable_id_padding
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.1.2
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-21 00:00:00.000000000 Z
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: -2248843498865931733
148
+ hash: -158942502906389966
130
149
  required_rubygems_version: !ruby/object:Gem::Requirement
131
150
  none: false
132
151
  requirements:
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- class TestDumpable < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
6
- end
7
- end