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 ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+
4
+ gem 'rails', '3.0.0.rc'
5
+ gem 'sqlite3-ruby'
6
+ gem 'minitest'
7
+ gem 'mysql'
8
+ gem 'growl'
9
+ gem 'redgreen'
10
+ gem 'mocha'
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
+
@@ -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,15 @@
1
+ module CopyMachine
2
+ module ActionControllerExtensions
3
+ module Base
4
+
5
+ def self.included(klass)
6
+ klass.before_filter :reset_copy_machine
7
+ end
8
+
9
+ def reset_copy_machine
10
+ ::CopyMachine.notifications = []
11
+ end
12
+
13
+ end
14
+ end
15
+ 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