pendulum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39768125e6cbdf710a729beac0133f6c3e53d7b9
4
+ data.tar.gz: ef90d7cedb538420b6cdae2dcea602a2b5bafb5b
5
+ SHA512:
6
+ metadata.gz: b9e1c70253542545c488fd040d7640f907343920783feae932def202a27f274f23781251f060fb3deeeff382b49910eb2b723a681f47a3750c9df53cd3535556
7
+ data.tar.gz: da9651c21dc64f7b674b94b2f66edd0f88c869744191c149493bf2a3918f2b42faad57ac958ca842e549a6c02ee4075e86313e73d42631dd3f300a103e993f98
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pendulum.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 monochromegane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Pendulum
2
+
3
+ Pendulum is a tool to manage Treasure Data scheduled jobs.
4
+
5
+ It defines the state of Treasure Data scheduled jobs using DSL, and updates the jobs according as DSL.
6
+
7
+ ## Usage
8
+
9
+ ```sh
10
+ # Export from Treasure Data
11
+ $ pendulum --apikey='...' -e -o Schedfile
12
+
13
+ # Update Schedfile
14
+ $ vi Schedfile
15
+
16
+ # Apply scheduled jobs
17
+ $ pendulum --apikey='...' -a --dry-run
18
+ $ pendulum --apikey='...' -a
19
+ ```
20
+
21
+ ## Schedfile
22
+
23
+ ```rb
24
+ schedule 'test-scheduled-job' do
25
+ database 'db_name'
26
+ query 'select time from access;'
27
+ retry_limit 0
28
+ priority :normal
29
+ cron '30 0 * * *'
30
+ timezone 'Asia/Tokyo'
31
+ delay 0
32
+ result_url 'td://@/db_name/table_name'
33
+ end
34
+ ```
35
+
36
+ #### query_file
37
+
38
+ If your query is long, you can specify `query_file`.
39
+
40
+ ```rb
41
+ query_file 'queries/test-scheduled-job.hql'
42
+ ```
43
+
44
+ #### result
45
+
46
+ You can use `result` DSL instead of `result_url`.
47
+
48
+ ```rb
49
+ schedule 'test-scheduled-job' do
50
+ database 'db_name'
51
+ ...
52
+ result :td do
53
+ database 'db_name'
54
+ table 'table_name'
55
+ end
56
+ end
57
+ ```
58
+
59
+ Now, Pendulum supports `td` and `postgresql` result export.
60
+ If you want to use other result export, please send pull request :smile:
61
+
62
+ ## Installation
63
+
64
+ Add this line to your application's Gemfile:
65
+
66
+ ```ruby
67
+ gem 'pendulum'
68
+ ```
69
+
70
+ And then execute:
71
+
72
+ $ bundle
73
+
74
+ Or install it yourself as:
75
+
76
+ $ gem install pendulum
77
+
78
+ ## Development
79
+
80
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
81
+
82
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
83
+
84
+ ## Contributing
85
+
86
+ Bug reports and pull requests are welcome on GitHub at https://github.com/monochromegane/pendulum.
87
+
88
+
89
+ ## License
90
+
91
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
92
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pendulum"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/pendulum ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'pendulum'
5
+
6
+ exit_code = Pendulum::Runner.new.run ARGV
7
+ exit exit_code
data/lib/pendulum.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "pendulum/version"
2
+ require "pendulum/client"
3
+ require "pendulum/settings"
4
+ require "pendulum/configuration"
5
+ require "pendulum/dsl/helper"
6
+ require "pendulum/dsl/schedule"
7
+ require "pendulum/dsl/result"
8
+ require "pendulum/dsl/output/base"
9
+ require "pendulum/dsl/output/treasure_data"
10
+ require "pendulum/dsl/output/postgresql"
11
+ require "pendulum/dsl/output/result"
12
+ require "pendulum/dsl/converter"
13
+ require "pendulum/command/apply"
14
+ require "pendulum/command/apply/result_url"
15
+ require "pendulum/command/apply/schedule"
16
+ require "pendulum/runner"
17
+
18
+ module Pendulum
19
+ end
@@ -0,0 +1,50 @@
1
+ require 'td'
2
+ require 'td-client'
3
+ require 'fileutils'
4
+
5
+ module Pendulum
6
+ class Client
7
+ def initialize(api_key='', options={}, &block)
8
+ @api_key = api_key
9
+ @config = Configuration.new(options)
10
+ @config.instance_eval(&block) if block_given?
11
+ end
12
+
13
+ def apply(dry_run: false, force: false, color: false)
14
+ Pendulum::Command::Apply.new(
15
+ td_client,
16
+ current_schedules,
17
+ @config.schedules,
18
+ dry_run,
19
+ force,
20
+ color,
21
+ ).execute
22
+ end
23
+
24
+ def export(output)
25
+ result = DSL::Converter.new(td_client.schedules).convert
26
+ # schedule
27
+ File.write(output, result[:schedule])
28
+ # queries
29
+ query_dir = File.join(File.dirname(output), 'queries')
30
+ make_dir(query_dir)
31
+ result[:queries].each do |query|
32
+ File.write(File.join(query_dir, query[:name]), query[:query])
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def current_schedules
39
+ td_client.schedules
40
+ end
41
+
42
+ def td_client
43
+ @td_client ||= TreasureData::Client.new(@api_key, {ssl: true})
44
+ end
45
+
46
+ def make_dir(dir)
47
+ FileUtils.mkdir(dir) unless File.exist?(dir)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ module Pendulum::Command
2
+ class Apply
3
+ attr_accessor :client, :dry_run, :force
4
+
5
+ def initialize(client, from, to, dry_run=false, force=false, color=false)
6
+ @schedules = matched_schedules(client, from, to, dry_run, force, color)
7
+ end
8
+
9
+ def execute
10
+ @schedules.each{|s| s.apply }
11
+ end
12
+
13
+ private
14
+
15
+ def matched_schedules(client, from, to, dry_run, force, color)
16
+ # create or update
17
+ schedules = to.map do |schedule|
18
+ Schedule.new(
19
+ client,
20
+ from.find{|f| f.name == schedule.name},
21
+ schedule,
22
+ dry_run,
23
+ force,
24
+ color
25
+ )
26
+ end
27
+
28
+ # delete
29
+ from.reject{|f| to.any?{|t| t.name == f.name}}.each do |schedule|
30
+ schedules << Schedule.new(client, schedule, nil, dry_run, force, color)
31
+ end
32
+ schedules
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ module Pendulum::Command
2
+ class Apply
3
+ class ResultURL
4
+ attr_accessor :client, :from, :to
5
+ def initialize(client, from, to)
6
+ self.client = client
7
+ self.from = from
8
+ self.to = to
9
+ end
10
+
11
+ def changed?
12
+ from_uri = to_uri(from)
13
+ to_uri = mask(to_uri(to))
14
+
15
+ uri_without_query(from_uri) != uri_without_query(to_uri) ||
16
+ query_hash(from_uri) != query_hash(to_uri)
17
+ end
18
+
19
+ def mask(uri)
20
+ uri.password = '***' if uri.user
21
+ uri
22
+ end
23
+
24
+ private
25
+
26
+ def uri_without_query(uri)
27
+ uri.to_s.split('?').first
28
+ end
29
+
30
+ def query_hash(uri)
31
+ Hash[URI::decode_www_form(uri.query || '')]
32
+ end
33
+
34
+ def to_uri(url)
35
+ return URI.parse(url) if url.include?('://')
36
+
37
+ # use result
38
+ name, table = url.split(':', 2)
39
+
40
+ result = result_by(name)
41
+ return URI.parse(url) unless result
42
+
43
+ uri = URI.parse(result.url)
44
+ uri.path += "/#{table}"
45
+ uri
46
+ end
47
+
48
+ def results
49
+ @results ||= client.results
50
+ end
51
+
52
+ def result_by(name)
53
+ results.find{|r| name == r.name}
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,165 @@
1
+ require 'highline'
2
+
3
+ module Pendulum::Command
4
+ class Apply
5
+ class Schedule
6
+ attr_accessor :client, :from, :to, :dry_run, :force, :color
7
+ def initialize(client, from, to, dry_run=false, force=false, color=false)
8
+ self.client = client
9
+ self.from = from
10
+ self.to = to
11
+ self.dry_run = dry_run
12
+ self.force = force
13
+ self.color = color
14
+ end
15
+
16
+ def apply
17
+ case
18
+ when will_create? then create
19
+ when will_update? then update
20
+ when will_delete? then delete
21
+ end
22
+ end
23
+
24
+ def create
25
+ puts message_for_create
26
+ client.create_schedule(to.name, to.to_params) unless dry_run?
27
+ end
28
+
29
+ def update
30
+ puts message_for_update
31
+ puts message_for_diff if has_diff?
32
+ if force? || has_diff?
33
+ client.update_schedule(to.name, to.to_params) unless dry_run?
34
+ end
35
+ end
36
+
37
+ def delete
38
+ puts message_for_delete
39
+ client.delete_schedule(from.name) if force? && !dry_run?
40
+ end
41
+
42
+ private
43
+
44
+ def will_create?
45
+ !from && to
46
+ end
47
+
48
+ def will_update?
49
+ from && to
50
+ end
51
+
52
+ def will_delete?
53
+ from && !to
54
+ end
55
+
56
+ def has_diff?
57
+ !diff.empty?
58
+ end
59
+
60
+ def diff
61
+ return {} unless will_update?
62
+
63
+ @diff ||= begin
64
+ default_params.merge(to.to_params).select do |k, v|
65
+ if k == :result
66
+ result_url_changed?(from.result_url, v)
67
+ else
68
+ v != from.send(k)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def masked_diff
75
+ return diff unless diff.key?(:result)
76
+
77
+ masked = diff.dup
78
+ uri = URI.parse(masked[:result])
79
+ uri.password = '***' if uri.user
80
+ masked[:result] = uri.to_s
81
+
82
+ masked
83
+ end
84
+
85
+ def message_for_create
86
+ colorize message_for(:create), :cyan
87
+ end
88
+
89
+ def message_for_update
90
+ if force? || has_diff?
91
+ colorize message_for(:update), :green
92
+ else
93
+ colorize message_with_dry_run("No change schedule: #{name}"), :blue
94
+ end
95
+ end
96
+
97
+ def message_for_delete
98
+ if force?
99
+ colorize message_for(:delete), :red
100
+ else
101
+ colorize message_with_dry_run("Undefined schedule (pass `--force` if you want to remove): #{name}"), :yellow
102
+ end
103
+ end
104
+
105
+ def message_with_dry_run(message)
106
+ message += ' (dry-run)' if dry_run?
107
+ message
108
+ end
109
+
110
+ def message_for(action)
111
+ message_with_dry_run "#{action.to_s.capitalize} schedule: #{name}"
112
+ end
113
+
114
+ def message_for_diff
115
+ message = masked_diff.map do |name, value|
116
+ " set #{name}=#{value}"
117
+ end.join("\n")
118
+ colorize message, :green
119
+ end
120
+
121
+ def name
122
+ (from && from.name) || (to && to.name)
123
+ end
124
+
125
+ def dry_run?
126
+ dry_run
127
+ end
128
+
129
+ def force?
130
+ force
131
+ end
132
+
133
+ def color?
134
+ color
135
+ end
136
+
137
+ def default_params
138
+ {
139
+ database: '',
140
+ query: nil,
141
+ retry_limit: 0,
142
+ priority: 0,
143
+ cron: nil,
144
+ timezone: 'Asia/Tokyo', # TODO: require timezone.
145
+ delay: 0,
146
+ result: ''
147
+ }
148
+ end
149
+
150
+ def result_url_changed?(from_url, to_url)
151
+ Apply::ResultURL.new(client, from_url, to_url).changed?
152
+ end
153
+
154
+ def colorize(message, color)
155
+ return message unless color?
156
+ h.color message, color
157
+ end
158
+
159
+ def h
160
+ @h ||= HighLine.new
161
+ end
162
+ end
163
+ end
164
+ end
165
+
@@ -0,0 +1,18 @@
1
+ module Pendulum
2
+ class Configuration
3
+ def initialize(options={})
4
+ settings = Pendulum::Settings.load(options[:env])
5
+ if file = options[:file]
6
+ self.instance_eval(File.read(file), file)
7
+ end
8
+ end
9
+
10
+ def schedule(name, &block)
11
+ schedules << DSL::Schedule.new(name, &block)
12
+ end
13
+
14
+ def schedules
15
+ @schedules ||= []
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ require 'erb'
2
+
3
+ module Pendulum::DSL
4
+ class Converter
5
+ def initialize(schedules)
6
+ @schedules = schedules
7
+ end
8
+
9
+ def convert
10
+ result = {}
11
+ result[:schedule] = @schedules.map do |schedule|
12
+ to_dsl(schedule)
13
+ end.join("\n")
14
+
15
+ result[:queries] = @schedules.map do |schedule|
16
+ to_query(schedule)
17
+ end.compact
18
+
19
+ result
20
+ end
21
+
22
+ private
23
+
24
+ def to_dsl(schedule)
25
+ ERB.new(<<-EOS, nil, '-').result(binding)
26
+ schedule '<%= schedule.name %>' do
27
+ database '<%= schedule.database %>'
28
+ <% if schedule.query -%>
29
+ query_file 'queries/<%= schedule.name %>.hql'
30
+ # type :hive # FIXME: Treasure Data schedule api dosen't contain type result.
31
+ retry_limit <%= schedule.retry_limit %>
32
+ priority <%= schedule.priority %>
33
+ <% end -%>
34
+ <% if schedule.cron -%>
35
+ cron '<%= schedule.cron %>'
36
+ timezone '<%= schedule.timezone %>'
37
+ delay <%= schedule.delay %>
38
+ <% end -%>
39
+ <% if schedule.result_url != '' -%>
40
+ result_url '<%= schedule.result_url %>'
41
+ <% end -%>
42
+ end
43
+ EOS
44
+ end
45
+
46
+ def to_query(schedule)
47
+ return nil unless schedule.query
48
+ {name: "#{schedule.name}.hql", query: schedule.query}
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ module Pendulum::DSL
2
+ module Helper
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def define_setter(*names)
9
+ names.each do |name|
10
+ define_method(name) do |value|
11
+ instance_variable_set("@#{name}", value)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module Pendulum::DSL::Output
2
+ class Base
3
+ include Pendulum::DSL::Helper
4
+
5
+ def initialize(&block)
6
+ self.instance_eval(&block) if block_given?
7
+ end
8
+
9
+ def to_url
10
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
11
+ end
12
+
13
+ private
14
+
15
+ def with_options(url, *options)
16
+ params = (options || []).select do |option|
17
+ instance_variable_defined?("@#{option}")
18
+ end.map do |option|
19
+ "#{option}=#{instance_variable_get("@#{option}")}"
20
+ end.join('&')
21
+ url + (params.empty? ? '' : "?#{params}")
22
+ end
23
+
24
+ def username_and_password
25
+ case
26
+ when @username && @password
27
+ "#{@username}:#{@password}"
28
+ when @username
29
+ @username
30
+ when @password
31
+ ":#{@password}"
32
+ end
33
+ end
34
+
35
+ def hostname_and_port
36
+ case
37
+ when @hostname && @port
38
+ "#{@hostname}:#{@port}"
39
+ when @hostname
40
+ @hostname
41
+ when @port
42
+ ":#{@port}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module Pendulum::DSL::Output
2
+ class Postgresql < Base
3
+ define_setter :username, :password, :hostname, :port,
4
+ :database, :table, :ssl, :schema, :mode, :method
5
+
6
+ def to_url
7
+ url = "postgresql://#{username_and_password}@#{hostname_and_port}/#{@database}/#{@table}"
8
+ with_options(url, :ssl, :schema, :mode, :method)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module Pendulum::DSL::Output
2
+ class Result < Base
3
+ define_setter :table
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ super()
8
+ end
9
+
10
+ def to_url
11
+ "#{@name}:#{@table}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Pendulum::DSL::Output
2
+ class TreasureData < Base
3
+ define_setter :database, :table, :mode
4
+
5
+ def to_url
6
+ with_options("td://@/#{@database}/#{@table}", :mode)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Pendulum::DSL
2
+ class Result
3
+ attr_accessor :type, :output
4
+
5
+ def initialize(type, &block)
6
+ self.type = type
7
+ self.output = output_by(type)
8
+ self.output.instance_eval(&block) if block_given?
9
+ end
10
+
11
+ def to_url
12
+ output.to_url
13
+ end
14
+
15
+ private
16
+
17
+ def output_by(type)
18
+ case type.to_sym
19
+ when :treasure_data, :td
20
+ Output::TreasureData.new
21
+ when :postgresql
22
+ Output::Postgresql.new
23
+ else
24
+ Output::Result.new(type)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ module Pendulum::DSL
2
+ class Schedule
3
+ include Helper
4
+
5
+ attr_accessor :name
6
+
7
+ def initialize(name, &block)
8
+ self.name = name
9
+ self.instance_eval(&block) if block_given?
10
+ end
11
+
12
+ define_setter :database, :query, :timezone,
13
+ :delay, :retry_limit, :type
14
+
15
+ def query_file(path)
16
+ query(File.read(path))
17
+ end
18
+
19
+ def cron(cron)
20
+ @cron = %i(hourly daily monthly).include?(cron) ? "@#{cron}" : cron
21
+ end
22
+
23
+ def priority(priority)
24
+ @priority = priority.is_a?(Integer) ? priority : priority_id_of(priority)
25
+ end
26
+
27
+ def result_url(url)
28
+ @result = url
29
+ end
30
+
31
+ def result(type, &block)
32
+ result = Result.new(type, &block)
33
+ @result = result.to_url
34
+ end
35
+
36
+ def to_params
37
+ instance_variables.inject({}) do |params, v|
38
+ params[v.to_s.delete('@').to_sym] = instance_variable_get(v)
39
+ params
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def priority_id_of(name)
46
+ case name.to_sym
47
+ when :very_low then -2
48
+ when :low then -1
49
+ when :normal then 0
50
+ when :high then 1
51
+ when :very_high then 2
52
+ else 0
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ require 'optparse'
2
+
3
+
4
+ module Pendulum
5
+ DEFAULT_SCHEDFILE = 'Schedfile'
6
+ class Runner
7
+ def run(argv=ARGV)
8
+ api_key = nil
9
+ mode = nil
10
+ dry_run = false
11
+ force = false
12
+ color = true
13
+ options = {
14
+ file: DEFAULT_SCHEDFILE,
15
+ env: :development,
16
+ }
17
+ output = DEFAULT_SCHEDFILE
18
+
19
+ opt.on('-k', '--apikey=KEY') {|v| api_key = v }
20
+
21
+ # apply
22
+ opt.on('-a', '--apply') { mode = :apply }
23
+ opt.on('-E', '--environment=ENV') {|v| options[:env] = v }
24
+ opt.on('-f', '--file=FILE') {|v| options[:file] = v }
25
+ opt.on('', '--dry-run') { dry_run = true }
26
+ opt.on('', '--force') { force = true }
27
+ opt.on('', '--no-color') { color = false }
28
+
29
+ # export
30
+ opt.on('-e', '--export') do
31
+ mode = :export
32
+ options.delete(:file)
33
+ end
34
+ opt.on('-o', '--output=FILE') {|v| output = v }
35
+
36
+ opt.parse!(argv) rescue return usage $!
37
+ return usage if (api_key.nil? || mode.nil?)
38
+
39
+ begin
40
+ client = Client.new(api_key, options)
41
+ case mode
42
+ when :apply
43
+ client.apply(dry_run: dry_run, force: force, color: color)
44
+ when :export
45
+ client.export(output)
46
+ end
47
+ rescue
48
+ $stderr.puts $!
49
+ return 1
50
+ end
51
+
52
+ return 0
53
+ end
54
+
55
+ private
56
+
57
+ def usage(err=nil)
58
+ puts err if err
59
+ puts opt.help
60
+ err ? 1 : 0
61
+ end
62
+
63
+ def opt
64
+ @opt ||= OptionParser.new
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,23 @@
1
+ require 'hashie'
2
+
3
+ module Pendulum
4
+ class Settings
5
+ class << self
6
+ def load(env)
7
+ merge(load_from(:default), load_from(env))
8
+ end
9
+
10
+ private
11
+
12
+ def load_from(env)
13
+ path = File.join('environments', "#{env}.yml")
14
+ return Hashie::Mash.new unless File.file?(path)
15
+ Hashie::Mash.load(path)
16
+ end
17
+
18
+ def merge(org, new)
19
+ org.deep_merge(new)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Pendulum
2
+ VERSION = "0.1.0"
3
+ end
data/pendulum.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pendulum/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pendulum"
8
+ spec.version = Pendulum::VERSION
9
+ spec.authors = ["monochromegane"]
10
+ spec.email = ["dev.kuro.obi@gmail.com"]
11
+
12
+ spec.summary = %q{Pendulum is a tool to manage Treasure Data scheduled jobs.}
13
+ spec.description = %q{Pendulum is a tool to manage Treasure Data scheduled jobs.}
14
+ spec.homepage = "https://github.com/monochromegane/pendulum"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "td"
23
+ spec.add_dependency "td-client"
24
+ spec.add_dependency "hashie"
25
+ spec.add_dependency "highline"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.11"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "minitest", "~> 5.0"
30
+ spec.add_development_dependency "pry"
31
+ end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pendulum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - monochromegane
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: td
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: td-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hashie
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: highline
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '5.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Pendulum is a tool to manage Treasure Data scheduled jobs.
126
+ email:
127
+ - dev.kuro.obi@gmail.com
128
+ executables:
129
+ - pendulum
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - exe/pendulum
142
+ - lib/pendulum.rb
143
+ - lib/pendulum/client.rb
144
+ - lib/pendulum/command/apply.rb
145
+ - lib/pendulum/command/apply/result_url.rb
146
+ - lib/pendulum/command/apply/schedule.rb
147
+ - lib/pendulum/configuration.rb
148
+ - lib/pendulum/dsl/converter.rb
149
+ - lib/pendulum/dsl/helper.rb
150
+ - lib/pendulum/dsl/output/base.rb
151
+ - lib/pendulum/dsl/output/postgresql.rb
152
+ - lib/pendulum/dsl/output/result.rb
153
+ - lib/pendulum/dsl/output/treasure_data.rb
154
+ - lib/pendulum/dsl/result.rb
155
+ - lib/pendulum/dsl/schedule.rb
156
+ - lib/pendulum/runner.rb
157
+ - lib/pendulum/settings.rb
158
+ - lib/pendulum/version.rb
159
+ - pendulum.gemspec
160
+ homepage: https://github.com/monochromegane/pendulum
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.4.5.1
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Pendulum is a tool to manage Treasure Data scheduled jobs.
184
+ test_files: []