copy_machine 0.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/.document +5 -0
- data/.gitignore +21 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/copy_machine.gemspec +80 -0
- data/lib/copy_machine.rb +24 -0
- data/lib/copy_machine/action_controller_extensions/base.rb +15 -0
- data/lib/copy_machine/active_record_extensions/base.rb +79 -0
- data/lib/copy_machine/active_record_extensions/callbacks.rb +59 -0
- data/lib/copy_machine/active_record_extensions/constraints.rb +25 -0
- data/lib/copy_machine/autoloading.rb +18 -0
- data/lib/copy_machine/configuration.rb +20 -0
- data/lib/copy_machine/railtie.rb +65 -0
- data/lib/copy_machine/template.rb +20 -0
- data/lib/generators/copy_machine_configuration_generator.rb +7 -0
- data/lib/generators/copy_machine_template_generator.rb +10 -0
- data/lib/generators/templates/copy_machine.rb +16 -0
- data/lib/generators/templates/example_template.rb +39 -0
- data/lib/rack/copy_machine.rb +44 -0
- data/lib/rack/copy_machine/asset_server.rb +16 -0
- data/lib/rack/copy_machine/public/__rack_copy_machine__/copy_machine.css +65 -0
- data/lib/rack/copy_machine/public/__rack_copy_machine__/copy_machine.js +8 -0
- data/lib/rack/copy_machine/public/__rack_copy_machine__/jquery-1.4.2.min.js +154 -0
- data/lib/rack/views/notifications.html.erb +29 -0
- data/test/configuration_test.rb +28 -0
- data/test/copy_machine_integration_test.rb +39 -0
- data/test/database.yml +7 -0
- data/test/helper.rb +63 -0
- metadata +128 -0
data/.document
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionmailer (3.0.0.rc)
|
6
|
+
actionpack (= 3.0.0.rc)
|
7
|
+
mail (~> 2.2.5)
|
8
|
+
actionpack (3.0.0.rc)
|
9
|
+
activemodel (= 3.0.0.rc)
|
10
|
+
activesupport (= 3.0.0.rc)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
erubis (~> 2.6.6)
|
13
|
+
i18n (~> 0.4.1)
|
14
|
+
rack (~> 1.2.1)
|
15
|
+
rack-mount (~> 0.6.9)
|
16
|
+
rack-test (~> 0.5.4)
|
17
|
+
tzinfo (~> 0.3.22)
|
18
|
+
activemodel (3.0.0.rc)
|
19
|
+
activesupport (= 3.0.0.rc)
|
20
|
+
builder (~> 2.1.2)
|
21
|
+
i18n (~> 0.4.1)
|
22
|
+
activerecord (3.0.0.rc)
|
23
|
+
activemodel (= 3.0.0.rc)
|
24
|
+
activesupport (= 3.0.0.rc)
|
25
|
+
arel (~> 0.4.0)
|
26
|
+
tzinfo (~> 0.3.22)
|
27
|
+
activeresource (3.0.0.rc)
|
28
|
+
activemodel (= 3.0.0.rc)
|
29
|
+
activesupport (= 3.0.0.rc)
|
30
|
+
activesupport (3.0.0.rc)
|
31
|
+
arel (0.4.0)
|
32
|
+
activesupport (>= 3.0.0.beta)
|
33
|
+
builder (2.1.2)
|
34
|
+
erubis (2.6.6)
|
35
|
+
abstract (>= 1.0.0)
|
36
|
+
growl (1.0.3)
|
37
|
+
i18n (0.4.1)
|
38
|
+
mail (2.2.5)
|
39
|
+
activesupport (>= 2.3.6)
|
40
|
+
mime-types
|
41
|
+
treetop (>= 1.4.5)
|
42
|
+
mime-types (1.16)
|
43
|
+
minitest (1.7.0)
|
44
|
+
mocha (0.9.8)
|
45
|
+
rake
|
46
|
+
mysql (2.8.1)
|
47
|
+
polyglot (0.3.1)
|
48
|
+
rack (1.2.1)
|
49
|
+
rack-mount (0.6.9)
|
50
|
+
rack (>= 1.0.0)
|
51
|
+
rack-test (0.5.4)
|
52
|
+
rack (>= 1.0)
|
53
|
+
rails (3.0.0.rc)
|
54
|
+
actionmailer (= 3.0.0.rc)
|
55
|
+
actionpack (= 3.0.0.rc)
|
56
|
+
activerecord (= 3.0.0.rc)
|
57
|
+
activeresource (= 3.0.0.rc)
|
58
|
+
activesupport (= 3.0.0.rc)
|
59
|
+
bundler (>= 1.0.0.rc.1)
|
60
|
+
railties (= 3.0.0.rc)
|
61
|
+
railties (3.0.0.rc)
|
62
|
+
actionpack (= 3.0.0.rc)
|
63
|
+
activesupport (= 3.0.0.rc)
|
64
|
+
rake (>= 0.8.3)
|
65
|
+
thor (~> 0.14.0)
|
66
|
+
rake (0.8.7)
|
67
|
+
redgreen (1.2.2)
|
68
|
+
sqlite3-ruby (1.3.1)
|
69
|
+
thor (0.14.0)
|
70
|
+
treetop (1.4.8)
|
71
|
+
polyglot (>= 0.3.1)
|
72
|
+
tzinfo (0.3.22)
|
73
|
+
|
74
|
+
PLATFORMS
|
75
|
+
ruby
|
76
|
+
|
77
|
+
DEPENDENCIES
|
78
|
+
growl
|
79
|
+
minitest
|
80
|
+
mocha
|
81
|
+
mysql
|
82
|
+
rails (= 3.0.0.rc)
|
83
|
+
redgreen
|
84
|
+
sqlite3-ruby
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 richmolj@gmail.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
Copy Machine
|
2
|
+
============
|
3
|
+
|
4
|
+
Ever had to pull records from production to debug an issue, or had a sprawling legacy app with no seeds.rb and a need for development data? Using CopyMachine, this is fixed in two ways - either dynamically pull data from a slave database whenever no records are found, or define dataset templates that pull in data with a rake task. Whenever a copy happens, you'll get a growl notice, a small button in the top left of your app, or both. These will contain the sql and line number that fired the copy (click the button for additional info).
|
5
|
+
|
6
|
+
The main benefits of CopyMachine are 1) No hassles and easy set up 2) Easily pull associations and 3) never fire callbacks or validations.
|
7
|
+
|
8
|
+
Install
|
9
|
+
-------
|
10
|
+
|
11
|
+
Make sure you have growl and growl_notifications set up.
|
12
|
+
|
13
|
+
**Gemfile**
|
14
|
+
|
15
|
+
gem 'growl'
|
16
|
+
gem 'copy_machine'
|
17
|
+
|
18
|
+
Then, add a 'copy_machine' entry to your database.yml for the alternate database you'd like to copy from.
|
19
|
+
|
20
|
+
**Console**
|
21
|
+
|
22
|
+
rails g copy_machine_configuration
|
23
|
+
|
24
|
+
To create config/initializers/copy_machine.rb
|
25
|
+
|
26
|
+
As you move through your app, hits to the database that return 0 records will attempt to copy records from the :copy_machine database from database.yml
|
27
|
+
|
28
|
+
Datasets
|
29
|
+
--------
|
30
|
+
|
31
|
+
If you want to pull in specific sets of records, define a template that finds this data and run it via rake.
|
32
|
+
|
33
|
+
**console**
|
34
|
+
|
35
|
+
rails g copy_machine_template person
|
36
|
+
|
37
|
+
Gives you a templates directory off of Rails root that contains person_template.rb. Within this file:
|
38
|
+
|
39
|
+
**Template**
|
40
|
+
|
41
|
+
define_dataset :by_id, :needs => [:id] do
|
42
|
+
Person.find(@id)
|
43
|
+
end
|
44
|
+
|
45
|
+
That you can call from rake
|
46
|
+
|
47
|
+
rake copy:person[by_id]
|
48
|
+
|
49
|
+
You'll get prompted for the id you want to copy, and your response will set the @id instance variable. Similarly,
|
50
|
+
|
51
|
+
**templates/my_model_template.rb**
|
52
|
+
|
53
|
+
define_dataset :foo, :needs => [:bar] do
|
54
|
+
MyModel.find_by_bar(@bar)
|
55
|
+
end
|
56
|
+
|
57
|
+
Could be called by
|
58
|
+
|
59
|
+
rake copy:my_model[foo]
|
60
|
+
|
61
|
+
Which would prompt for foo and set @foo. You can call any associations or methods you need here, just like in your app. See the comments in the generated template for more examples.
|
62
|
+
|
63
|
+
Configuration
|
64
|
+
-------------
|
65
|
+
|
66
|
+
Because apps can get funky, you may have special needs before or after the copy process takes place, or scrubbing the sql itself. There are hooks in place for this:
|
67
|
+
|
68
|
+
**config/initializers/copy_machine.rb**
|
69
|
+
|
70
|
+
config.alter_sql do |sql|
|
71
|
+
# scrub the sql, ie sql.gsub(/whatever/,'somethingelse')
|
72
|
+
end
|
73
|
+
|
74
|
+
config.before_copy do
|
75
|
+
# your code
|
76
|
+
end
|
77
|
+
|
78
|
+
config.after_copy do
|
79
|
+
# your code
|
80
|
+
end
|
81
|
+
|
82
|
+
Note on Patches/Pull Requests
|
83
|
+
-----------------------------
|
84
|
+
|
85
|
+
* Fork the project.
|
86
|
+
* Make your feature addition or bug fix.
|
87
|
+
* Add tests for it. This is important so I don't break it in a
|
88
|
+
future version unintentionally.
|
89
|
+
* Commit, do not mess with rakefile, version, or history.
|
90
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
91
|
+
* Send me a pull request. Bonus points for topic branches.
|
92
|
+
|
93
|
+
Copyright
|
94
|
+
---------
|
95
|
+
|
96
|
+
Copyright (c) 2010 richmolj@gmail.com. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "copy_machine"
|
8
|
+
gem.summary = %Q{Easily copy production or slave records into your development database}
|
9
|
+
gem.description = %Q{For sprawling legacy apps, seeds.rb and fixtures are sometimes not enough. Copy machine eases development by copying records from a slave database as you move through your app, or as you execute predefined templates}
|
10
|
+
gem.email = "richmolj@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/richmolj/copy_machine"
|
12
|
+
gem.authors = ["Lee Richmond"]
|
13
|
+
gem.add_dependency 'growl'
|
14
|
+
gem.add_development_dependency "minitest", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/*_test.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "copy_machine #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{copy_machine}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Lee Richmond"]
|
12
|
+
s.date = %q{2010-10-07}
|
13
|
+
s.description = %q{For sprawling legacy apps, seeds.rb and fixtures are sometimes not enough. Copy machine eases development by copying records from a slave database as you move through your app, or as you execute predefined templates}
|
14
|
+
s.email = %q{richmolj@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"copy_machine.gemspec",
|
29
|
+
"lib/copy_machine.rb",
|
30
|
+
"lib/copy_machine/action_controller_extensions/base.rb",
|
31
|
+
"lib/copy_machine/active_record_extensions/base.rb",
|
32
|
+
"lib/copy_machine/active_record_extensions/callbacks.rb",
|
33
|
+
"lib/copy_machine/active_record_extensions/constraints.rb",
|
34
|
+
"lib/copy_machine/autoloading.rb",
|
35
|
+
"lib/copy_machine/configuration.rb",
|
36
|
+
"lib/copy_machine/railtie.rb",
|
37
|
+
"lib/copy_machine/template.rb",
|
38
|
+
"lib/generators/copy_machine_configuration_generator.rb",
|
39
|
+
"lib/generators/copy_machine_template_generator.rb",
|
40
|
+
"lib/generators/templates/copy_machine.rb",
|
41
|
+
"lib/generators/templates/example_template.rb",
|
42
|
+
"lib/rack/copy_machine.rb",
|
43
|
+
"lib/rack/copy_machine/asset_server.rb",
|
44
|
+
"lib/rack/copy_machine/public/__rack_copy_machine__/copy_machine.css",
|
45
|
+
"lib/rack/copy_machine/public/__rack_copy_machine__/copy_machine.js",
|
46
|
+
"lib/rack/copy_machine/public/__rack_copy_machine__/jquery-1.4.2.min.js",
|
47
|
+
"lib/rack/views/notifications.html.erb",
|
48
|
+
"test/configuration_test.rb",
|
49
|
+
"test/copy_machine_integration_test.rb",
|
50
|
+
"test/database.yml",
|
51
|
+
"test/helper.rb"
|
52
|
+
]
|
53
|
+
s.homepage = %q{http://github.com/richmolj/copy_machine}
|
54
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
55
|
+
s.require_paths = ["lib"]
|
56
|
+
s.rubygems_version = %q{1.3.7}
|
57
|
+
s.summary = %q{Easily copy production or slave records into your development database}
|
58
|
+
s.test_files = [
|
59
|
+
"test/configuration_test.rb",
|
60
|
+
"test/copy_machine_integration_test.rb",
|
61
|
+
"test/helper.rb"
|
62
|
+
]
|
63
|
+
|
64
|
+
if s.respond_to? :specification_version then
|
65
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
66
|
+
s.specification_version = 3
|
67
|
+
|
68
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
69
|
+
s.add_runtime_dependency(%q<growl>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
73
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
77
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
data/lib/copy_machine.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'copy_machine/autoloading'
|
2
|
+
require 'copy_machine/railtie'
|
3
|
+
|
4
|
+
module CopyMachine
|
5
|
+
mattr_accessor :notifications
|
6
|
+
self.notifications = []
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def configure
|
10
|
+
yield CopyMachine::Configuration.config
|
11
|
+
end
|
12
|
+
|
13
|
+
def config
|
14
|
+
CopyMachine::Configuration.config
|
15
|
+
end
|
16
|
+
|
17
|
+
def growl_notification(message)
|
18
|
+
Growl.send("notify_warning", message, {
|
19
|
+
:title => "Copy Machine",
|
20
|
+
:sticky => false
|
21
|
+
})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module CopyMachine
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
module Base
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend CopyMachine::ActiveRecordExtensions::Callbacks
|
7
|
+
klass.extend CopyMachine::ActiveRecordExtensions::Constraints
|
8
|
+
|
9
|
+
klass.extend ClassMethods
|
10
|
+
klass.singleton_class.alias_method_chain :find_by_sql, :override
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def with_alternate_connection(sql, backtrace)
|
15
|
+
ActiveSupport::Notifications.instrument("copy_machine.sql",
|
16
|
+
:sql => sql, :name => self.name, :backtrace => backtrace) do
|
17
|
+
|
18
|
+
ActiveRecord::Base.establish_connection :copy_machine
|
19
|
+
yield
|
20
|
+
ActiveRecord::Base.establish_connection Rails.env
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_by_sql_with_override(sql)
|
25
|
+
records = find_by_sql_without_override(sql)
|
26
|
+
|
27
|
+
if records.empty? and !@avoid_copy
|
28
|
+
backtrace = Rails.backtrace_cleaner.clean(caller).first
|
29
|
+
backtrace = backtrace.split(':')[0..1].join(":") unless backtrace.nil?
|
30
|
+
|
31
|
+
copy_mode(sql, backtrace) do
|
32
|
+
unless (proc = CopyMachine.config.sql_alteration).nil?
|
33
|
+
sql = proc.call(sql)
|
34
|
+
end
|
35
|
+
records = find_by_sql_without_override(sql)
|
36
|
+
end
|
37
|
+
|
38
|
+
records.each { |r| cp r }
|
39
|
+
end
|
40
|
+
|
41
|
+
records
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def cp(record)
|
47
|
+
record.class.reset_column_information
|
48
|
+
attrs = build_copy_attributes(record)
|
49
|
+
new_record = new(attrs)
|
50
|
+
new_record.id = record.id # have to set here or will be nil
|
51
|
+
|
52
|
+
@avoid_copy = true
|
53
|
+
new_record.save(:validate => false) unless exists?(new_record.id)
|
54
|
+
@avoid_copy = false
|
55
|
+
new_record
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: obscurity logic here
|
59
|
+
def build_copy_attributes(record)
|
60
|
+
record.attributes.reject { |k,v| !record.class.column_names.include?(k) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def copy_mode(sql, backtrace)
|
64
|
+
with_alternate_connection(sql, backtrace) do
|
65
|
+
hook = CopyMachine.config.before_copy_hook
|
66
|
+
instance_eval &hook unless hook.nil?
|
67
|
+
|
68
|
+
yield
|
69
|
+
|
70
|
+
hook = CopyMachine.config.after_copy_hook
|
71
|
+
instance_eval &hook unless hook.nil?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|