dynosaur 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4143dd8410c842565956f544fca8c5edcfc2752c
4
- data.tar.gz: 4d24ef4ccbc6a6fe6742da89b1f39585f7c0759f
3
+ metadata.gz: 1448d342f89404da10452e0e95176627f5c7ed56
4
+ data.tar.gz: ffe0d95b07ad64c410d89e4645289faa107c7cf5
5
5
  SHA512:
6
- metadata.gz: c153e19d0bdeea037f565d44a2bec05d8c2e8911eb2c4e73f39ca53ec9ba73d12bf1b4c6f795705eb11d48c635a503d567ad84988e5c2350f14b1de45ed26975
7
- data.tar.gz: f7257bd138dc326167639de8665e018bc2d216b9c2746c268b8d41a530abbb781cbbc11d874467cb2e4e0966635e979fc4beb28ba7cc3bc5ee3031c66973cb75
6
+ metadata.gz: 06a80b1d43a568785eee94acab44b02a260d4a61e83bd2502886ab2abd1dc0c2c9bf647b37b8a23ddec22a260455024a66ffd4192df89fdb7139af767387cfce
7
+ data.tar.gz: f0d630594d61500d65d7eafa207c33c36ac622d5af7f89fd63ebb0d7c6dda2649aa7ca20a1c65bcd198f92acf8bb4b4aaeaa2ad20bea3e9ff3593ffd0e18c030
data/README.md CHANGED
@@ -35,6 +35,13 @@ local_process.start
35
35
  # => 48345
36
36
  ```
37
37
 
38
+ You can also check to see if a similar rake task is already running before starting
39
+ the task (e.g. if you want at most one instance of that task running concurrently)
40
+
41
+ ```ruby
42
+ dyno = Dynosaur::Process::Heroku.new(task: 'session:destroy', args: [2500])
43
+ dyno.start unless dyno.running?
44
+ ```
38
45
 
39
46
  ## Development
40
47
 
data/Rakefile CHANGED
@@ -5,22 +5,3 @@ require 'rspec/core/rake_task'
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  task default: :spec
8
-
9
- task :echo, [:arg1, :arg2] do |_task, args|
10
- puts %Q(ARG1: "#{args[:arg1]}")
11
- puts %Q(ARG2: "#{args[:arg2]}")
12
- end
13
-
14
- task :sleep, [:duration] do |_task, args|
15
- File.open('/tmp/sleep', 'w') do |file|
16
- file.puts "Sleeping"
17
- args[:duration].to_i.times do
18
- sleep(1)
19
- file.print('.')
20
- file.flush
21
- end
22
- file.print("\n")
23
- file.puts "Awake"
24
- file.flush
25
- end
26
- end
@@ -0,0 +1,34 @@
1
+ # Detects if a running dyno is currently executing the rake command
2
+ module Dynosaur
3
+ class Process
4
+ class Heroku
5
+ class Finder
6
+ def initialize(rake_command:, client: Client::HerokuClient.client)
7
+ @rake_command = rake_command
8
+ @client = client
9
+ end
10
+
11
+ def exists?
12
+ one_off_dynos.any? do |dyno|
13
+ Utils::RakeCommand.valid?(dyno.command) &&
14
+ Utils::RakeCommand.parse(dyno.command) == rake_command
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :rake_command, :client
21
+
22
+ # @return [Array] Object instances, which respond to #command, for all
23
+ # locally one off dynos
24
+ def one_off_dynos
25
+ app_name = Dynosaur::Client::HerokuClient.app_name
26
+ dynos = client.dyno.list(app_name).map do |response|
27
+ Struct.new(:type, :command).new(response['type'], response['command'])
28
+ end
29
+ dynos.select { |dyno| dyno.type == 'run' }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,5 @@
1
1
  require 'dynosaur/client/heroku_client'
2
2
 
3
- require_relative '../process'
4
-
5
3
  # Start a detached rake task on a one-off dyno
6
4
  module Dynosaur
7
5
  class Process
@@ -23,10 +21,10 @@ module Dynosaur
23
21
 
24
22
  def start
25
23
  app_name = Dynosaur::Client::HerokuClient.app_name
26
- dyno_accessor = Dynosaur::Client::HerokuClient.client.dyno
27
- create_opts = { command: rake_command, attach: false }
24
+ client = Dynosaur::Client::HerokuClient.client
25
+ create_opts = { command: rake_command.to_s, attach: false }
28
26
  create_opts[:size] = size if size
29
- response = dyno_accessor.create(app_name, create_opts)
27
+ response = client.dyno.create(app_name, create_opts)
30
28
  response['name']
31
29
  end
32
30
 
@@ -0,0 +1,23 @@
1
+ # Detects if a running process is currently executing the rake command
2
+ module Dynosaur
3
+ class Process
4
+ class Local
5
+ class Finder
6
+ def initialize(rake_command:)
7
+ @rake_command = rake_command
8
+ end
9
+
10
+ def exists?
11
+ Utils::OS.structured_ps.any? do |process|
12
+ Utils::RakeCommand.valid?(process.command) &&
13
+ Utils::RakeCommand.parse(process.command) == rake_command
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :rake_command
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,10 +1,9 @@
1
- require_relative '../process'
2
-
1
+ # Start the rake task in a local process
3
2
  module Dynosaur
4
3
  class Process
5
4
  class Local < Process
6
5
  def start
7
- pid = ::Process.spawn(rake_command)
6
+ pid = ::Process.spawn(rake_command.to_s)
8
7
  ::Process.detach(pid)
9
8
  pid
10
9
  end
@@ -3,29 +3,26 @@ require 'shellwords'
3
3
  module Dynosaur
4
4
  class Process
5
5
  def initialize(task:, args: [], opts: {})
6
- @task = task
7
- @args = args
6
+ @rake_command = Utils::RakeCommand.new(task: task, args: args)
8
7
  after_initialize(opts)
9
8
  end
10
9
 
10
+ def running?
11
+ klass = self.class.const_get('Finder')
12
+ finder = klass.new(rake_command: rake_command)
13
+ finder.exists?
14
+ end
15
+
11
16
  def start
12
17
  fail NotImplementedError, 'This method must be implemented in a subclass'
13
18
  end
14
19
 
15
20
  private
16
21
 
17
- attr_reader :args, :task
22
+ attr_reader :rake_command
18
23
 
19
24
  def after_initialize(opts)
20
25
  # Do nothing so subclasses don't have to override if they want a no-op
21
26
  end
22
-
23
- def rake_command
24
- formatted_args = args.map do |arg|
25
- arg.is_a?(String) ? arg.shellescape : arg
26
- end.join(',')
27
- task_with_args = args.empty? ? task : "#{task}[#{formatted_args}]"
28
- "rake #{task_with_args} --trace"
29
- end
30
27
  end
31
28
  end
@@ -0,0 +1,24 @@
1
+ # Utility methods for interacting with the underlying OS
2
+ module Dynosaur
3
+ module Utils
4
+ module OS
5
+ class << self
6
+ # @return [Array] Object instances, which respond to #command, for all
7
+ # locally running processes returned by the ps command
8
+ def structured_ps
9
+ ps_array = command_ps.split("\n").map(&:strip)
10
+ ps_array.shift # Remove the header row
11
+ ps_array.map do |command|
12
+ Struct.new(:command).new(command)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def command_ps
19
+ `ps -o command`
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ # Utility methods for generating and parsing rake command
2
+ module Dynosaur
3
+ module Utils
4
+ class RakeCommand
5
+ PARSE_REGEX = /\brake\s+([\w:]+)(\[.*\])?/
6
+
7
+ attr_reader :task, :args
8
+
9
+ # @param command [String] the command to test for validity
10
+ # @return [true|false] true iff the string is a valid rake command
11
+ def self.valid?(command)
12
+ !(command =~ PARSE_REGEX).nil?
13
+ end
14
+
15
+ # @param command [String] the command to parse
16
+ # @return [RakeCommand] an instance that represents the command being parsed
17
+ # @raise [ArgumentError] if the command is not a valid rake command
18
+ def self.parse(command)
19
+ match = command.match(PARSE_REGEX)
20
+ raise ArgumentError, %Q(Invalid rake command: "#{command}") unless match
21
+ task = match[1]
22
+ args = match[2] ? match[2].tr('[]', '').split(',') : []
23
+ new(task: task, args: args)
24
+ end
25
+
26
+ def initialize(task:, args: [])
27
+ @task = task
28
+ @args = args
29
+ end
30
+
31
+ # @return [String] the full rake command, including arguments
32
+ def to_s
33
+ formatted_args = args.map do |arg|
34
+ arg.is_a?(String) ? arg.shellescape : arg
35
+ end.join(',')
36
+ task_with_args = args.empty? ? task : "#{task}[#{formatted_args}]"
37
+ "rake #{task_with_args} --trace"
38
+ end
39
+
40
+ def ==(other)
41
+ task == other.task && args.map(&:to_s) == other.args.map(&:to_s)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,4 +1,4 @@
1
1
  # Gem version number
2
2
  module Dynosaur
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
data/lib/dynosaur.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  require 'dynosaur/version'
2
+
3
+ require 'dynosaur/process'
2
4
  require 'dynosaur/process/heroku'
5
+ require 'dynosaur/process/heroku/finder'
3
6
  require 'dynosaur/process/local'
7
+ require 'dynosaur/process/local/finder'
8
+
9
+ require 'dynosaur/utils/os'
10
+ require 'dynosaur/utils/rake_command'
4
11
 
5
12
  # Spin up a rake task in a separate process
6
13
  module Dynosaur
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynosaur
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Collier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-10 00:00:00.000000000 Z
11
+ date: 2015-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: platform-api
@@ -117,7 +117,11 @@ files:
117
117
  - lib/dynosaur/client/heroku_client.rb
118
118
  - lib/dynosaur/process.rb
119
119
  - lib/dynosaur/process/heroku.rb
120
+ - lib/dynosaur/process/heroku/finder.rb
120
121
  - lib/dynosaur/process/local.rb
122
+ - lib/dynosaur/process/local/finder.rb
123
+ - lib/dynosaur/utils/os.rb
124
+ - lib/dynosaur/utils/rake_command.rb
121
125
  - lib/dynosaur/version.rb
122
126
  homepage:
123
127
  licenses: