leda 0.0.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.
@@ -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