replicate 1.0 → 1.1

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/HACKING ADDED
@@ -0,0 +1,61 @@
1
+ Grab a local clone of rtomayko/replicate:
2
+
3
+ git clone git://github.com/rtomayko/replicate.git
4
+ cd replicate
5
+
6
+ The default rake task installs the latest supported activerecord
7
+ version to a project local GEM_HOME=./vendor and runs the unit
8
+ tests:
9
+
10
+ $ rake
11
+ installing activerecord-3.1.0 to ./vendor/1.8
12
+ installing sqlite3 to ./vendor/1.8
13
+ Using activerecord 3.1.0
14
+ Loaded suite ...
15
+ Started
16
+ ....................
17
+ Finished in 0.150186 seconds.
18
+
19
+ 20 tests, 106 assertions, 0 failures, 0 errors
20
+
21
+ Use `rake test:all' to run tests under all activerecord versions:
22
+
23
+ $ rake test:all
24
+ installing activerecord ~> 2.2.3 to ./vendor
25
+ installing activerecord ~> 2.3.14 to ./vendor
26
+ installing activerecord ~> 3.0.10 to ./vendor
27
+ installing activerecord ~> 3.1.0 to ./vendor
28
+ ==> testing activerecord ~> 2.2.3
29
+ Started
30
+ ....................
31
+ Finished in 0.119517 seconds.
32
+
33
+ 20 tests, 106 assertions, 0 failures, 0 errors
34
+ ==> testing activerecord ~> 2.3.14
35
+ Started
36
+ ....................
37
+ Finished in 0.119517 seconds.
38
+
39
+ 20 tests, 106 assertions, 0 failures, 0 errors
40
+ <snip>
41
+
42
+ rake test:all should always be passing under latest stable MRI
43
+ 1.8.7 and MRI 1.9.x.
44
+
45
+ Running individual test files directly requires setting the
46
+ GEM_HOME environment variable and ensuring ./lib is on the load
47
+ path:
48
+
49
+ export GEM_HOME=vendor/1.9 # or 1.8.7
50
+ ruby -Ilib test/active_record_test.rb
51
+
52
+ You can also control which activerecord version is used in the
53
+ test with the AR_VERSION environment variable:
54
+
55
+ rake setup:all
56
+ export GEM_HOME=vendor/1.8.7
57
+ AR_VERSION=3.1.0 ruby -rubygems -Ilib test/active_record_test.rb
58
+
59
+ If you have something worth sharing, please send a pull request:
60
+
61
+ https://github.com/rtomayko/replicate
data/README.md CHANGED
@@ -1,15 +1,20 @@
1
- # Replicate
2
-
3
1
  Dump and load relational objects between Ruby environments.
2
+ ===========================================================
3
+
4
+ The project started at GitHub to simplify the process of getting real production
5
+ data into development and staging environments. We use it to replicate entire
6
+ repository data (including associated issue, pull request, commit comment, etc.
7
+ records) from production to our development environments with a single command.
8
+ It's excessively useful for troubleshooting issues, support requests, and
9
+ exception reports as well as for establishing real data for evaluating design
10
+ concepts.
4
11
 
5
- The project was started at GitHub to ease the process of getting real production
6
- data into staging and development environments. We have a custom command that
7
- uses the replicate machinery to dump entire repository data (including
8
- associated objects like issues, pull requests, commit comments, etc.) from
9
- production and load it into the current environment. This is excessively useful
10
- for troubleshooting issues, support requests, and exception reports.
12
+ Synopsis
13
+ --------
11
14
 
12
- ## Synopsis
15
+ Installing:
16
+
17
+ $ gem install replicate
13
18
 
14
19
  Dumping objects:
15
20
 
@@ -32,16 +37,34 @@ Dumping and loading over SSH:
32
37
  $ remote_command="replicate -r /app/config/environment -d 'User.find(1234)'"
33
38
  $ ssh example.org "$remote_command" |replicate -r config/environment -l
34
39
 
35
- ## ActiveRecord
40
+ ActiveRecord
41
+ ------------
42
+
43
+ Basic support for dumping and loading ActiveRecord objects is included. The
44
+ tests pass under ActiveRecord versions 2.2.3, 2.3.14, 3.0.10, and 3.1.0 under
45
+ MRI 1.8.7 as well as under MRI 1.9.2.
46
+
47
+ To use customization macros in your models, require the replicate library after
48
+ ActiveRecord (in e.g., `config/initializers/libraries.rb`):
49
+
50
+ require 'active_record'
51
+ require 'replicate'
52
+
53
+ ActiveRecord support works sensibly without customization so this isn't strictly
54
+ necessary to use the `replicate` command. The following sections document the
55
+ available customization macros.
36
56
 
37
- *NOTE: Replicate has been tested only under ActiveRecord 2.2. Support for
38
- ActiveRecord 3.x is planned.*
57
+ ### Association Dumping
39
58
 
40
- Basic support for dumping and loading ActiveRecord objects is included. When an
41
- object is dumped, all `belongs_to` and `has_one` associations are automatically
42
- followed and included in the dump. You can mark `has_many` and
43
- `has_and_belongs_to_many` associations for automatic inclusion using the
44
- `replicate_associations` macro:
59
+ The baked in support adds some more or less sensible default behavior for all
60
+ subclasses of `ActiveRecord::Base` such that dumping an object will bring in
61
+ objects related via `belongs_to` and `has_one` associations.
62
+
63
+ Unlike 1:1 associations, `has_many` and `has_and_belongs_to_many` associations
64
+ are not automatically included. Doing so would quickly lead to the entire
65
+ database being sucked in. It can be useful to mark specific associations for
66
+ automatic inclusion using the `replicate_associations` macro. For instance,
67
+ to always include `EmailAddress` records belonging to a `User`:
45
68
 
46
69
  class User < ActiveRecord::Base
47
70
  belongs_to :profile
@@ -50,10 +73,13 @@ followed and included in the dump. You can mark `has_many` and
50
73
  replicate_associations :email_addresses
51
74
  end
52
75
 
53
- By default, the loader attempts to create a new record for all objects. This can
54
- lead to unique constraint errors when a record already exists with matching
55
- attributes. To update existing records instead of always creating new ones,
56
- define a natural key for the model using the `replicate_natural_key` macro:
76
+ ### Natural Keys
77
+
78
+ By default, the loader attempts to create a new record with a new primary key id
79
+ for all objects. This can lead to unique constraint errors when a record already
80
+ exists with matching attributes. To update existing records instead of
81
+ creating new ones, define a natural key for the model using the `replicate_natural_key`
82
+ macro:
57
83
 
58
84
  class User < ActiveRecord::Base
59
85
  belongs_to :profile
@@ -63,13 +89,45 @@ define a natural key for the model using the `replicate_natural_key` macro:
63
89
  replicate_associations :email_addresses
64
90
  end
65
91
 
66
- Multiple attribute names may be specified to define a compound key.
92
+ class EmailAddress < ActiveRecord::Base
93
+ belongs_to :user
94
+ replicate_natural_key :user_id, :email
95
+ end
96
+
97
+ Multiple attribute names may be specified to define a compound key. Foreign key
98
+ column attributes (`user_id`) are often included in natural keys.
99
+
100
+ ### Validations and Callbacks
101
+
102
+ __IMPORTANT:__ All ActiveRecord validations and callbacks are disabled on the
103
+ loading side. While replicate piggybacks on AR for relationship information and
104
+ uses `ActiveRecord::Base#save` to write objects to the database, it's designed
105
+ to act as a simple dump / load tool.
106
+
107
+ It's sometimes useful to run certain types of callbacks on replicate. For
108
+ instance, you might want to create files on disk or load information into a
109
+ separate data store any time an object enters the database. The best way to go
110
+ about this currently is to override the model's `load_replicant` class method:
111
+
112
+ class User < ActiveRecord::Base
113
+ def self.load_replicant(type, id, attrs)
114
+ id, object = super
115
+ object.register_in_redis
116
+ object.some_other_callback
117
+ [id, object]
118
+ end
119
+ end
120
+
121
+ This interface will be improved in future versions.
67
122
 
68
- ## Custom Objects
123
+ Custom Objects
124
+ --------------
69
125
 
70
126
  Other object types may be included in the dump stream so long as they implement
71
127
  the `dump_replicant` and `load_replicant` methods.
72
128
 
129
+ ### dump_replicant
130
+
73
131
  The dump side calls `#dump_replicant(dumper)` on each object. The method must
74
132
  call `dumper.write()` with the class name, id, and hash of primitively typed
75
133
  attributes for the object:
@@ -84,6 +142,8 @@ attributes for the object:
84
142
  end
85
143
  end
86
144
 
145
+ ### load_replicant
146
+
87
147
  The load side calls `::load_replicant(type, id, attributes)` on the class to
88
148
  load each object into the current environment. The method must return an
89
149
  `[id, object]` tuple:
@@ -97,3 +157,28 @@ load each object into the current environment. The method must return an
97
157
  [user.id, user]
98
158
  end
99
159
  end
160
+
161
+ How it works
162
+ ------------
163
+
164
+ The dump format is designed for streaming relational data. Each object is
165
+ encoded as a `[type, id, attributes]` tuple and marshalled directly onto the
166
+ stream. The `type` (class name string) and `id` must form a distinct key when
167
+ combined, `attributes` must consist of only string keys and simply typed values.
168
+
169
+ Relationships between objects in the stream are managed as follows:
170
+
171
+ - An object's attributes may encode references to objects that precede it
172
+ in the stream using a simple tuple format: [:id, 'User', 1234].
173
+
174
+ - The dump side ensures that objects are written to the dump stream in
175
+ "reference order" such that when an object A includes a reference attribute
176
+ to an object B, B is guaranteed to arrive before A.
177
+
178
+ - The load side maintains a mapping of ids from the dumping system to the newly
179
+ replicated objects on the loading system. When the loader encounters a
180
+ reference value [:id, 'User', 1234] in an object's attributes, it converts it
181
+ to the load side id value.
182
+
183
+ Dumping and loading happens in a streaming fashion. There is no limit on the
184
+ number of objects included in the stream.
data/Rakefile CHANGED
@@ -1,6 +1,71 @@
1
- task :default => :test
1
+ require 'rbconfig'
2
+ require 'rake/clean'
3
+ task :default => [:setup, :test]
4
+
5
+ vendor_dir = './vendor'
6
+ ruby_version = RbConfig::CONFIG['ruby_version']
7
+ ENV['GEM_HOME'] = "#{vendor_dir}/#{ruby_version}"
8
+
9
+ desc "Install gem dependencies for development"
10
+ task :setup => 'setup:latest' do
11
+ verbose(false) { gem_install 'sqlite3' }
12
+ end
2
13
 
3
14
  desc "Run tests"
4
15
  task :test do
16
+ ENV['RUBYOPT'] = [ENV['RUBYOPT'], 'rubygems'].compact.join(' ')
17
+ ENV['RUBYLIB'] = ['lib', ENV['RUBYLIB']].compact.join(':')
5
18
  sh "testrb test/*_test.rb", :verbose => false
6
19
  end
20
+ CLEAN.include 'test/db'
21
+
22
+ desc "Build gem"
23
+ task :build do
24
+ sh "gem build replicate.gemspec"
25
+ end
26
+
27
+ # supported activerecord gem versions
28
+ AR_VERSIONS = []
29
+ AR_VERSIONS << '2.2.3' if RUBY_VERSION < '1.9'
30
+ AR_VERSIONS.concat %w[2.3.14 3.0.10 3.1.0]
31
+
32
+ desc "Run unit tests under all supported AR versions"
33
+ task 'test:all' => 'setup:all' do
34
+ failures = []
35
+ AR_VERSIONS.each do |vers|
36
+ warn "==> testing activerecord ~> #{vers}"
37
+ ENV['AR_VERSION'] = vers
38
+ ok = system("rake -s test")
39
+ failures << vers if !ok
40
+ warn ''
41
+ end
42
+ fail "activerecord version failures: #{failures.join(', ')}" if failures.any?
43
+ end
44
+
45
+ # file tasks for installing each AR version
46
+ desc 'Install gem dependencies for all supported AR versions'
47
+ task 'setup:all' => 'setup'
48
+ AR_VERSIONS.each do |vers|
49
+ version_file = "#{ENV['GEM_HOME']}/versions/activerecord-#{vers}"
50
+ file version_file do |f|
51
+ verbose(false) { gem_install 'activerecord', vers }
52
+ end
53
+ task "setup:#{vers}" => version_file
54
+ task "setup:all" => "setup:#{vers}"
55
+ end
56
+ task "setup:latest" => "setup:#{AR_VERSIONS.last}"
57
+ CLEAN.include 'vendor'
58
+
59
+ # Install a gem to the local GEM_HOME but only if it isn't already installed
60
+ def gem_install(name, version = nil)
61
+ version_name = [name, version].compact.join('-')
62
+ version_file = "#{ENV['GEM_HOME']}/versions/#{version_name}"
63
+ return if File.exist?(version_file)
64
+ warn "installing #{version_name} to #{ENV['GEM_HOME']}"
65
+ command = "gem install --no-rdoc --no-ri #{name}"
66
+ command += " -v '~> #{version}'" if version
67
+ command += " >/dev/null"
68
+ sh command
69
+ mkdir_p File.dirname(version_file)
70
+ File.open(version_file, 'wb') { }
71
+ end
data/bin/replicate CHANGED
@@ -10,6 +10,7 @@
10
10
  #/ -r, --require Require the library. Often used with 'config/environment'.
11
11
  #/ -d, --dump Dump the repository and all related objects to stdout.
12
12
  #/ -l, --load Load dump file data from stdin.
13
+ #/ -i, --keep-id Use replicated ids when loading dump file.
13
14
  #/
14
15
  #/ -v, --verbose Write more status output.
15
16
  #/ -q, --quiet Write less status output.
@@ -20,6 +21,7 @@ require 'optparse'
20
21
  mode = nil
21
22
  verbose = false
22
23
  quiet = false
24
+ keep_id = false
23
25
  out = $stdout
24
26
 
25
27
  # parse arguments
@@ -31,6 +33,7 @@ ARGV.options do |opts|
31
33
  opts.on("-r", "--require=f") { |file| require file }
32
34
  opts.on("-v", "--verbose") { verbose = true }
33
35
  opts.on("-q", "--quiet") { quiet = true }
36
+ opts.on("-i", "--keep-id") { keep_id = true }
34
37
  opts.on_tail("-h", "--help", &usage)
35
38
  opts.parse!
36
39
  end
@@ -38,8 +41,9 @@ end
38
41
  # load rails environment and replicator lib.
39
42
  require 'replicate'
40
43
 
41
- # hack to enable AR query cache
42
44
  if defined?(ActiveRecord::Base)
45
+ ActiveRecord::Base.replicate_id = keep_id
46
+ # hack to enable AR query cache
43
47
  ActiveRecord::ConnectionAdapters::QueryCache.
44
48
  send :attr_writer, :query_cache, :query_cache_enabled
45
49
  ActiveRecord::Base.connection.send(:query_cache=, {})
@@ -126,6 +126,18 @@ module Replicate
126
126
  @replicate_natural_key = attribute_names
127
127
  end
128
128
 
129
+ # Set or retrieve whether replicated object should keep its original id.
130
+ # When not set, replicated objects will be created with new id.
131
+ def replicate_id(boolean=nil)
132
+ self.replicate_id = boolean unless boolean.nil?
133
+ @replicate_id.nil? ? superclass.replicate_id : @replicate_id
134
+ end
135
+
136
+ # Set flag for replicating original id.
137
+ def replicate_id=(boolean)
138
+ @replicate_id = boolean
139
+ end
140
+
129
141
  # Load an individual record into the database. If the models defines a
130
142
  # replicate_natural_key then an existing record will be updated if found
131
143
  # instead of a new record being created.
@@ -156,17 +168,47 @@ module Replicate
156
168
 
157
169
  # Update an AR object's attributes and persist to the database without
158
170
  # running validations or callbacks.
171
+ #
172
+ # Returns the [id, object] tuple for the newly replicated objected.
159
173
  def create_or_update_replicant(instance, attributes)
160
- def instance.callback(*args);end # Rails 2.x hack to disable callbacks.
161
-
174
+ # write replicated attributes to the instance
162
175
  attributes.each do |key, value|
163
- next if key == primary_key
164
- instance.write_attribute key, value
176
+ next if key == primary_key and not replicate_id
177
+ instance.send :write_attribute, key, value
178
+ end
179
+
180
+ # save the instance bypassing all callbacks and validations
181
+ replicate_disable_callbacks instance
182
+ if ::ActiveRecord::VERSION::MAJOR >= 3
183
+ instance.save :validate => false
184
+ else
185
+ instance.save false
165
186
  end
166
187
 
167
- instance.save false
168
188
  [instance.id, instance]
169
189
  end
190
+
191
+ # Disable all callbacks on an ActiveRecord::Base instance. Only the
192
+ # instance is effected. There is no way to re-enable callbacks once
193
+ # they've been disabled on an object.
194
+ def replicate_disable_callbacks(instance)
195
+ if ::ActiveRecord::VERSION::MAJOR >= 3
196
+ # AR 3.1.x
197
+ def instance.run_callbacks(*args); yield; end
198
+
199
+ # AR 3.0.x
200
+ def instance._run_save_callbacks(*args); yield; end
201
+ def instance._run_create_callbacks(*args); yield; end
202
+ def instance._run_update_callbacks(*args); yield; end
203
+ else
204
+ # AR 2.x
205
+ def instance.callback(*args)
206
+ end
207
+ def instance.record_timestamps
208
+ false
209
+ end
210
+ end
211
+ end
170
212
  end
171
213
 
172
214
  # Special object used to dump the list of associated ids for a
@@ -213,5 +255,6 @@ module Replicate
213
255
  ::ActiveRecord::Base.send :extend, ClassMethods
214
256
  ::ActiveRecord::Base.replicate_associations = []
215
257
  ::ActiveRecord::Base.replicate_natural_key = []
258
+ ::ActiveRecord::Base.replicate_id = false
216
259
  end
217
260
  end
@@ -1,12 +1,22 @@
1
1
  require 'test/unit'
2
2
  require 'stringio'
3
+
4
+ # require a specific AR version.
5
+ version = ENV['AR_VERSION']
6
+ gem 'activerecord', "~> #{version}" if version
3
7
  require 'active_record'
8
+ require 'active_record/version'
9
+ warn "Using activerecord #{ActiveRecord::VERSION::STRING}"
10
+
11
+ # replicate must be loaded after AR
4
12
  require 'replicate'
5
13
 
14
+ # create the sqlite db on disk
6
15
  dbfile = File.expand_path('../db', __FILE__)
7
16
  File.unlink dbfile if File.exist?(dbfile)
8
17
  ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => dbfile)
9
18
 
19
+ # load schema
10
20
  ActiveRecord::Migration.verbose = false
11
21
  ActiveRecord::Schema.define do
12
22
  create_table "users", :force => true do |t|
@@ -28,25 +38,25 @@ ActiveRecord::Schema.define do
28
38
  end
29
39
  end
30
40
 
41
+ # models
31
42
  class User < ActiveRecord::Base
32
43
  has_one :profile, :dependent => :destroy
33
44
  has_many :emails, :dependent => :destroy, :order => 'id'
34
-
35
45
  replicate_natural_key :login
36
46
  end
37
47
 
38
48
  class Profile < ActiveRecord::Base
39
49
  belongs_to :user
40
-
41
50
  replicate_natural_key :user_id
42
51
  end
43
52
 
44
53
  class Email < ActiveRecord::Base
45
54
  belongs_to :user
46
-
47
55
  replicate_natural_key :user_id, :email
48
56
  end
49
57
 
58
+ # The test case loads some fixture data once and uses transaction rollback to
59
+ # reset fixture state for each test's setup.
50
60
  class ActiveRecordTest < Test::Unit::TestCase
51
61
  def setup
52
62
  self.class.fixtures
@@ -250,4 +260,93 @@ class ActiveRecordTest < Test::Unit::TestCase
250
260
  assert !user.emails.empty?, "#{login} has no emails" if login != 'tmm1'
251
261
  end
252
262
  end
263
+
264
+ def test_loading_with_replicating_id
265
+ objects = []
266
+ @dumper.listen do |type, id, attrs, obj|
267
+ objects << [type, id, attrs, obj] if type == 'User'
268
+ end
269
+
270
+ dumped_users = {}
271
+ %w[rtomayko kneath tmm1].each do |login|
272
+ user = User.find_by_login(login)
273
+ @dumper.dump user
274
+ dumped_users[login] = user
275
+ end
276
+ assert_equal 3, objects.size
277
+
278
+ User.destroy_all
279
+ User.replicate_id = false
280
+
281
+ # load everything back up
282
+ objects.each { |type, id, attrs, obj| User.load_replicant type, id, attrs }
283
+
284
+ user = User.find_by_login('rtomayko')
285
+ assert_not_equal dumped_users['rtomayko'].id, user.id
286
+
287
+ User.destroy_all
288
+ User.replicate_id = true
289
+
290
+ # load everything back up
291
+ objects.each { |type, id, attrs, obj| User.load_replicant type, id, attrs }
292
+
293
+ user = User.find_by_login('rtomayko')
294
+ assert_equal dumped_users['rtomayko'].id, user.id
295
+ end
296
+
297
+ def test_loader_saves_without_validations
298
+ # note when a record is saved with validations
299
+ ran_validations = false
300
+ User.class_eval { validate { ran_validations = true } }
301
+
302
+ # check our assumptions
303
+ user = User.create(:login => 'defunkt')
304
+ assert ran_validations, "should run validations here"
305
+ ran_validations = false
306
+
307
+ # load one and verify validations are not run
308
+ user = nil
309
+ @loader.listen { |type, id, attrs, obj| user = obj }
310
+ @loader.feed 'User', 1, 'login' => 'rtomayko'
311
+ assert_not_nil user
312
+ assert !ran_validations, 'validations should not run on save'
313
+ end
314
+
315
+ def test_loader_saves_without_callbacks
316
+ # note when a record is saved with callbacks
317
+ callbacks = false
318
+ User.class_eval { after_save { callbacks = true } }
319
+
320
+ # check our assumptions
321
+ user = User.create(:login => 'defunkt')
322
+ assert callbacks, "should run callbacks here"
323
+ callbacks = false
324
+
325
+ # load one and verify validations are not run
326
+ user = nil
327
+ @loader.listen { |type, id, attrs, obj| user = obj }
328
+ @loader.feed 'User', 1, 'login' => 'rtomayko'
329
+ assert_not_nil user
330
+ assert !callbacks, 'callbacks should not run on save'
331
+ end
332
+
333
+ def test_loader_saves_without_updating_created_at_timestamp
334
+ timestamp = Time.at((Time.now - (24 * 60 * 60)).to_i)
335
+ user = nil
336
+ @loader.listen { |type, id, attrs, obj| user = obj }
337
+ @loader.feed 'User', 23, 'login' => 'brianmario', 'created_at' => timestamp
338
+ assert_equal timestamp, user.created_at
339
+ user = User.find(user.id)
340
+ assert_equal timestamp, user.created_at
341
+ end
342
+
343
+ def test_loader_saves_without_updating_updated_at_timestamp
344
+ timestamp = Time.at((Time.now - (24 * 60 * 60)).to_i)
345
+ user = nil
346
+ @loader.listen { |type, id, attrs, obj| user = obj }
347
+ @loader.feed 'User', 29, 'login' => 'rtomayko', 'updated_at' => timestamp
348
+ assert_equal timestamp, user.updated_at
349
+ user = User.find(user.id)
350
+ assert_equal timestamp, user.updated_at
351
+ end
253
352
  end
data/test/dumper_test.rb CHANGED
@@ -46,6 +46,7 @@ class DumperTest < Test::Unit::TestCase
46
46
 
47
47
  def test_writing_to_io
48
48
  io = StringIO.new
49
+ io.set_encoding 'BINARY' if io.respond_to?(:set_encoding)
49
50
  @dumper.marshal_to io
50
51
  @dumper.dump object = thing
51
52
  data = Marshal.dump(['Replicate::Object', object.id, object.attributes])
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: replicate
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 13
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 0
9
- version: "1.0"
8
+ - 1
9
+ version: "1.1"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ryan Tomayko
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-09-08 00:00:00 Z
17
+ date: 2011-09-09 00:00:00 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activerecord
@@ -24,15 +24,29 @@ dependencies:
24
24
  requirements:
25
25
  - - ~>
26
26
  - !ruby/object:Gem::Version
27
- hash: 7
27
+ hash: 5
28
28
  segments:
29
- - 2
30
- - 2
31
- version: "2.2"
29
+ - 3
30
+ - 1
31
+ version: "3.1"
32
32
  type: :development
33
33
  version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: sqlite3
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
34
48
  description: Dump and load relational objects between Ruby environments.
35
- email: r@tomayko.com
49
+ email: ryan@github.com
36
50
  executables:
37
51
  - replicate
38
52
  extensions: []
@@ -41,6 +55,7 @@ extra_rdoc_files: []
41
55
 
42
56
  files:
43
57
  - COPYING
58
+ - HACKING
44
59
  - README.md
45
60
  - Rakefile
46
61
  - bin/replicate
@@ -55,7 +70,7 @@ files:
55
70
  - test/dumper_test.rb
56
71
  - test/loader_test.rb
57
72
  - test/replicate_test.rb
58
- homepage: http://github.com/rtomayko/replicate/
73
+ homepage: http://github.com/rtomayko/replicate
59
74
  licenses: []
60
75
 
61
76
  post_install_message: