leda 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 432c8ae222a8f9c8802fadf82f52794c756c3860
4
+ data.tar.gz: 48a6bdb44438075b512f8b6bf86a1e80ed265b48
5
+ SHA512:
6
+ metadata.gz: ade3e8d94b1e082b218f459b70df6885c2de8811468fc373fd6d129ce72ed1f5e5e24ae34c660b775c6da731a33185afee39cfd81bab4590fbea13039fef44cf
7
+ data.tar.gz: 8fe10d3609461f86d1cd25c617e032398bbfd999a7dab98b6e3134916cf38ff4782408b4491743173d7d1d91dc4b010c70f4457021615396295aa0c5eaf20643
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/tmp
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in leda.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Rhett Sutphin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,91 @@
1
+ # Leda
2
+
3
+ Rake and capistrano tasks for clone data between Rails application environments.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'leda'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install leda
20
+
21
+ ## Usage
22
+
23
+ ### 60 second version
24
+
25
+ #### Define config/leda.rb
26
+
27
+ ```
28
+ Leda.configure do |leda|
29
+ leda.data_unit 'providers' do |du|
30
+ du.postgresql tables: %w(practices offices practitioners)
31
+ end
32
+ end
33
+ ```
34
+
35
+ #### Create rake tasks
36
+
37
+ In `lib/tasks/data.rake`:
38
+
39
+ ```
40
+ require 'leda'
41
+
42
+ load File.expand_path('../../../config/leda.rb', __FILE__)
43
+
44
+ namespace :data do
45
+ Leda.define_rake_tasks(:environment)
46
+ end
47
+ ```
48
+
49
+ #### Create cap tasks
50
+
51
+ (For Capistrano 3 only.)
52
+
53
+ In `lib/capistrano/tasks/data.cap`:
54
+
55
+ ```
56
+ require 'leda'
57
+
58
+ load File.expand_path('../../../../config/leda.rb', __FILE__)
59
+
60
+ namespace :data do
61
+ Leda.define_capistrano_tasks('data')
62
+ end
63
+ ```
64
+
65
+ #### Dump data via rake or capistrano
66
+
67
+ ```
68
+ $ bin/rake data:providers:dump
69
+ $ bin/cap production data:providers:dump
70
+ ```
71
+
72
+ #### Restore data via rake
73
+
74
+ ```
75
+ bin/rake data:providers:restore_from[production]
76
+ ```
77
+
78
+ ## Limitations
79
+
80
+ The current version of this library is tied to Rails (to discover the current
81
+ environment name) and particular libraries (for database configuration). A goal
82
+ for 1.0 would be to factor these things out so that Leda can be used in
83
+ standalone scripts also.
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it ( https://github.com/humanpractice/leda/fork )
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'leda/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "leda"
8
+ spec.version = Leda::VERSION
9
+ spec.authors = ["Rhett Sutphin"]
10
+ spec.email = ["rhett@detailedbalance.net"]
11
+ spec.summary = %q{Rake & capistrano tasks to clone partial data between Rails application environments.}
12
+ # spec.description = %q{TODO: Write a longer description. Optional.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+
25
+ spec.add_dependency 'activesupport', '>= 3.2'
26
+
27
+ # For ES
28
+ spec.add_dependency 'oj'
29
+ end
@@ -0,0 +1,37 @@
1
+ require "leda/version"
2
+
3
+ module Leda
4
+ autoload :Capistrano, 'leda/capistrano'
5
+ autoload :Configuration, 'leda/configuration'
6
+ autoload :DataUnit, 'leda/data_unit'
7
+ autoload :Rake, 'leda/rake'
8
+ autoload :Runner, 'leda/runner'
9
+ autoload :Store, 'leda/store'
10
+
11
+ class << self
12
+ def configuration
13
+ @configuration || reset_configuration
14
+ end
15
+
16
+ def reset_configuration
17
+ @configuration = ::Leda::Configuration.new
18
+ end
19
+
20
+ ##
21
+ # Builds up the global Leda configuration using the configuration DSL.
22
+ #
23
+ # Multiple invocations will add to the existing configuration. Call
24
+ # {#reset_configuration} to clear if desired.
25
+ def configure(&dsl)
26
+ configuration.update(&dsl)
27
+ end
28
+
29
+ def define_rake_tasks(*prerequisites)
30
+ ::Leda::Rake.define_tasks(configuration, prerequisites)
31
+ end
32
+
33
+ def define_capistrano_tasks(rake_task_namespace)
34
+ ::Leda::Capistrano.define_tasks(configuration, rake_task_namespace)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,84 @@
1
+ module Leda
2
+ module Capistrano
3
+ extend self
4
+
5
+ ##
6
+ # Defines capstrano tasks which run a dump remotely, compress it, download
7
+ # it, and expand it locally.
8
+ #
9
+ # As with {{Rake.define_tasks}}, it's probably best to run this inside a
10
+ # namespace.
11
+ def define_tasks(configuration, rake_task_namespace='data')
12
+ configuration.data_units.each do |data_unit|
13
+ t = ::Rake::Task.define_task [data_unit.name, 'dump'].join(':') do
14
+ current_env = fetch(:stage).to_s
15
+ runner = Runner.new(current_env, configuration)
16
+ tarball_directory = runner.relative_directory(current_env, data_unit)
17
+ tarball_path = tarball_directory.to_s + ".tar.bz2"
18
+ paths = runner.dump_relative_paths(data_unit.name)
19
+
20
+ # Create local target directory
21
+ run_locally do
22
+ execute :mkdir, '-p', tarball_directory.to_s
23
+ end
24
+
25
+ # dump & compress on server
26
+ on roles(:app) do
27
+ within(current_path) do
28
+ execute :rake, [rake_task_namespace, data_unit.name, 'dump'].compact.join(':')
29
+ execute :tar, 'cjvf', tarball_path, paths
30
+
31
+ expected_remote_filename = "#{current_path}/#{tarball_path}"
32
+ download! expected_remote_filename, tarball_path
33
+
34
+ execute :rm, tarball_path
35
+ end
36
+ end
37
+
38
+ # decompress locally
39
+ run_locally do
40
+ execute :tar, 'xjvf', tarball_path
41
+ end
42
+ end
43
+ t.add_description "Dump & download the remote #{data_unit.name} data"
44
+ end
45
+
46
+ t = ::Rake::Task.define_task 'dump' => configuration.data_units.map { |du| [rake_task_namespace, du.name, 'dump'].compact.join(':') }
47
+ t.add_description "Dump and download all remote data units"
48
+ end
49
+ end
50
+ end
51
+
52
+ __END__
53
+
54
+ namespace :data do
55
+ def execute_remote_dump(key, data_types)
56
+ paths_in_project = data_types.map { |ext| "db/data/#{key}.#{fetch :stage}.#{ext}" }
57
+ tarball_path = "db/data/#{key}.#{fetch :stage}.tar.bz2"
58
+
59
+ on roles(:app) do
60
+ within(current_path) do
61
+ execute :rake, "data:#{key}:dump"
62
+ execute :tar, 'cjvf', tarball_path, paths_in_project
63
+
64
+ expected_remote_filename = "#{current_path}/#{tarball_path}"
65
+ download! expected_remote_filename, tarball_path
66
+
67
+ execute :rm, tarball_path
68
+ end
69
+ end
70
+
71
+ # decompress locally
72
+ sh 'tar', 'xjvf', tarball_path
73
+ end
74
+
75
+ {
76
+ providers: %w(psql elasticsearch),
77
+ groups: %w(psql)
78
+ }.each do |key, data_types|
79
+ desc "Dump & download the remote #{key} data"
80
+ task "#{key}:dump" do
81
+ execute_remote_dump(key, data_types)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,91 @@
1
+ require 'leda'
2
+
3
+ require 'pathname'
4
+
5
+ module Leda
6
+ class Configuration
7
+ attr_reader :base_path
8
+
9
+ def initialize(&dsl)
10
+ @data_units_map = {}
11
+ @base_path = Pathname.new('db/leda')
12
+
13
+ if block_given?
14
+ update(&dsl)
15
+ end
16
+ end
17
+
18
+ def update(&dsl)
19
+ dsl.call(self)
20
+ self
21
+ end
22
+
23
+ def data_unit(name, &dsl)
24
+ data_unit = (@data_units_map[name] ||= DataUnit.new(name))
25
+
26
+ if block_given?
27
+ dsl.call(DataUnitConfigurator.new(data_unit))
28
+ end
29
+
30
+ data_unit
31
+ end
32
+
33
+ def data_units
34
+ @data_units_map.values
35
+ end
36
+
37
+ def project_root_dir
38
+ @project_root_dir or fail "Please set project_root_dir in your Leda configuration"
39
+ end
40
+
41
+ def project_root_dir=(path)
42
+ @project_root_dir = ensure_pathname(path)
43
+ end
44
+
45
+ def base_path=(path)
46
+ @base_path = ensure_pathname(path)
47
+ end
48
+
49
+ def base_dir
50
+ project_root_dir + base_path
51
+ end
52
+
53
+ private
54
+
55
+ def ensure_pathname(path)
56
+ case path
57
+ when Pathname
58
+ path
59
+ when nil
60
+ nil
61
+ else
62
+ Pathname.new(path.to_s)
63
+ end
64
+ end
65
+
66
+ ##
67
+ # @private
68
+ class DataUnitConfigurator
69
+ attr_reader :target
70
+
71
+ def initialize(data_unit)
72
+ @target = data_unit
73
+ end
74
+
75
+ def add_store(store_class, options)
76
+ options ||= {}
77
+
78
+ target.stores << store_class.new(options)
79
+ end
80
+
81
+ def method_missing(name, *args)
82
+ store_class = Store.find(name)
83
+ if store_class
84
+ add_store(store_class, args.first)
85
+ else
86
+ super
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,13 @@
1
+ require 'leda'
2
+
3
+ module Leda
4
+ class DataUnit
5
+ attr_reader :name
6
+ attr_reader :stores
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ @stores = []
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ require 'leda'
2
+ require 'rake'
3
+
4
+ module Leda
5
+ module Rake
6
+ extend self
7
+
8
+ ##
9
+ # Defines aggregate and individual dump & restore tasks based on the given
10
+ # {{Configuration}}. In real life, you'll probably want to call this from
11
+ # inside a `namespace` block:
12
+ #
13
+ # # In Rakefile
14
+ # namespace 'data' do
15
+ # Leda::Rake.define_tasks(Leda.configuration, [:environment])
16
+ # end
17
+ def define_tasks(configuration, outside_prerequisites=[])
18
+ define_dump_task(configuration, outside_prerequisites,
19
+ "Dump all Leda-configured data", nil, nil)
20
+ define_restore_from_task(configuration, outside_prerequisites,
21
+ "Restore all Leda-configured data from the specified env", nil, nil)
22
+
23
+ configuration.data_units.each do |data_unit|
24
+ define_dump_task(configuration, outside_prerequisites,
25
+ "Dump all data for #{data_unit.name} from the current env",
26
+ data_unit.name, nil)
27
+ define_restore_from_task(configuration, outside_prerequisites,
28
+ "Restore all data for #{data_unit.name} from the specified env into the current env",
29
+ data_unit.name, nil)
30
+
31
+ data_unit.stores.each do |store|
32
+ define_dump_task(configuration, outside_prerequisites,
33
+ "Dump all data from #{store.name} for #{data_unit.name} from the current env",
34
+ data_unit.name, store.name)
35
+ define_restore_from_task(configuration, outside_prerequisites,
36
+ "Restore all data into #{store.name} for #{data_unit.name} from the specified env into the current env",
37
+ data_unit.name, store.name)
38
+ end
39
+ end
40
+ end
41
+
42
+ def create_runner(configuration)
43
+ # TODO: externalize configuration
44
+ ::Leda::Runner.new(Rails.env, configuration)
45
+ end
46
+
47
+ def define_dump_task(configuration, outside_prerequisites, description, data_unit_name, store_name)
48
+ task_name = [data_unit_name, store_name, 'dump'].compact.join(':')
49
+
50
+ t = ::Rake::Task.define_task(task_name => outside_prerequisites) do
51
+ create_runner(configuration).dump(data_unit_name, store_name)
52
+ end
53
+ t.add_description description
54
+ end
55
+
56
+ def define_restore_from_task(configuration, outside_prerequisites, description, data_unit_name, store_name)
57
+ task_name = [data_unit_name, store_name, 'restore_from'].compact.join(':')
58
+
59
+ t = ::Rake::Task.define_task(task_name, [:source_env] => outside_prerequisites) do |t, args|
60
+ source_env = args[:source_env] or fail "Please specify the source env name"
61
+
62
+ create_runner(configuration).restore_from(source_env, data_unit_name, store_name)
63
+ end
64
+ t.add_description description
65
+ end
66
+ end
67
+ end