exel 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -0
  3. data/.rubocop_todo.yml +43 -0
  4. data/Guardfile +18 -11
  5. data/README.md +3 -0
  6. data/Rakefile +0 -1
  7. data/exel.gemspec +6 -5
  8. data/lib/exel/ast_node.rb +2 -2
  9. data/lib/exel/context.rb +16 -19
  10. data/lib/exel/deferred_context_value.rb +2 -2
  11. data/lib/exel/instruction.rb +2 -2
  12. data/lib/exel/job.rb +5 -5
  13. data/lib/exel/logging.rb +3 -3
  14. data/lib/exel/null_instruction.rb +1 -1
  15. data/lib/exel/processor_helper.rb +3 -5
  16. data/lib/exel/processors/async_processor.rb +3 -3
  17. data/lib/exel/providers/local_file_provider.rb +18 -0
  18. data/lib/exel/providers/threaded_async_provider.rb +13 -0
  19. data/lib/exel/{resource.rb → value.rb} +7 -11
  20. data/lib/exel/version.rb +1 -1
  21. data/lib/exel.rb +10 -1
  22. data/spec/exel/ast_node_spec.rb +3 -15
  23. data/spec/exel/context_spec.rb +40 -22
  24. data/spec/exel/job_spec.rb +8 -8
  25. data/spec/exel/logging_spec.rb +3 -3
  26. data/spec/exel/processors/async_processor_spec.rb +12 -4
  27. data/spec/exel/processors/split_processor_spec.rb +67 -67
  28. data/spec/exel/providers/local_async_provider_spec.rb +19 -0
  29. data/spec/exel/providers/local_file_provider_spec.rb +36 -0
  30. data/spec/exel/sequence_node_spec.rb +6 -13
  31. data/spec/exel/value_spec.rb +51 -0
  32. data/spec/exel_spec.rb +41 -0
  33. data/spec/spec_helper.rb +22 -3
  34. metadata +69 -40
  35. data/lib/exel/execution_worker.rb +0 -13
  36. data/lib/exel/handlers/s3_handler.rb +0 -43
  37. data/lib/exel/handlers/sidekiq_handler.rb +0 -21
  38. data/spec/exel/execution_worker_spec.rb +0 -13
  39. data/spec/exel/handlers/s3_handler_spec.rb +0 -49
  40. data/spec/exel/handlers/sidekiq_handler_spec.rb +0 -54
  41. data/spec/exel/resource_spec.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5eccac7b009cd6a4063a36cbb53b70de2efa22b
4
- data.tar.gz: f6e7dd493f08d09365158095290b0c319907215e
3
+ metadata.gz: 07a61d1ecdfd5d0d957caa0dd064631be8c3e546
4
+ data.tar.gz: 364c194ce4cb48938f1cc863b6bdf0a5b7e74e47
5
5
  SHA512:
6
- metadata.gz: 4135007934f8288843da2852a13cb269214725997069a3704b03ad7f4f8f5df103f7824156f1e959de24ca42d6b1a81e72f3fac1eb9b02e174ec9825b66f3b34
7
- data.tar.gz: d605c327290de5f1c71f4a4f552b71f7dc8d2469e146a5814e12581188011e6f462a4b08aa8434d2645e92cebaa76720bf0be14656dce1c0feb99c8e5f0e9ed6
6
+ metadata.gz: d004472bd8b4b3c6e496377c89cb261abcaa59fad5284009ecca65bee86dff428545af4ab27cd47ca74becbcdc9bb0a3d81962a67cf81f6437b5195a990cdf5f
7
+ data.tar.gz: debffc94d7f7fd53064f2b8f5317a68f3cbcaa4e961d19c6c2af630a31c39d79b4b42c111947e361a3f5a838853ff29d4a9814fb0892609da994f4c433517b85
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ require:
2
+ - rubocop-rspec
3
+ - rubocop/rspec/focused
4
+
5
+ inherit_from: .rubocop_todo.yml
6
+
7
+ AllCops:
8
+ DisplayStyleGuide: true
9
+
10
+ Metrics/LineLength:
11
+ Max: 120
12
+
13
+ Style/SpaceInsideHashLiteralBraces:
14
+ EnforcedStyle: no_space
15
+
16
+ RSpec/DescribedClass:
17
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,43 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-12-13 18:06:06 -0500 using RuboCop version 0.35.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: CountComments.
11
+ Metrics/MethodLength:
12
+ Max: 11
13
+
14
+ # Offense count: 2
15
+ # Configuration parameters: CountComments.
16
+ Metrics/ModuleLength:
17
+ Max: 166
18
+
19
+ # Offense count: 65
20
+ # Configuration parameters: CustomTransform, IgnoredWords.
21
+ RSpec/ExampleWording:
22
+ Exclude:
23
+ - 'spec/exel/ast_node_spec.rb'
24
+ - 'spec/exel/context_spec.rb'
25
+ - 'spec/exel/deferred_context_value_spec.rb'
26
+ - 'spec/exel/handlers/s3_handler_spec.rb'
27
+ - 'spec/exel/instruction_node_spec.rb'
28
+ - 'spec/exel/instruction_spec.rb'
29
+ - 'spec/exel/job_spec.rb'
30
+ - 'spec/exel/logging_spec.rb'
31
+ - 'spec/exel/processors/split_processor_spec.rb'
32
+ - 'spec/exel/resource_spec.rb'
33
+ - 'spec/exel/sequence_node_spec.rb'
34
+
35
+ # Offense count: 1
36
+ RSpec/InstanceVariable:
37
+ Exclude:
38
+ - 'spec/exel/logging_spec.rb'
39
+
40
+ # Offense count: 18
41
+ # Configuration parameters: Exclude.
42
+ Style/Documentation:
43
+ Enabled: false
data/Guardfile CHANGED
@@ -1,14 +1,21 @@
1
- guard :rspec, cmd: 'bundle exec rspec' do
2
- require 'guard/rspec/dsl'
3
- dsl = Guard::RSpec::Dsl.new(self)
1
+ group :rspec_rubocop, halt_on_fail: true do
2
+ guard :rspec, cmd: 'bundle exec rspec' do
3
+ require 'guard/rspec/dsl'
4
+ dsl = Guard::RSpec::Dsl.new(self)
4
5
 
5
- # RSpec files
6
- rspec = dsl.rspec
7
- watch(rspec.spec_helper) { rspec.spec_dir }
8
- watch(rspec.spec_support) { rspec.spec_dir }
9
- watch(rspec.spec_files)
6
+ # RSpec files
7
+ rspec = dsl.rspec
8
+ watch(rspec.spec_helper) { rspec.spec_dir }
9
+ watch(rspec.spec_support) { rspec.spec_dir }
10
+ watch(rspec.spec_files)
10
11
 
11
- # Ruby files
12
- ruby = dsl.ruby
13
- dsl.watch_spec_files_for(ruby.lib_files)
12
+ # Ruby files
13
+ ruby = dsl.ruby
14
+ dsl.watch_spec_files_for(ruby.lib_files)
15
+ end
16
+
17
+ guard :rubocop do
18
+ watch(/.+\.rb$/)
19
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
20
+ end
14
21
  end
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # EXEL
2
+ [![Gem Version](https://badge.fury.io/rb/exel.svg)](https://badge.fury.io/rb/exel)
3
+ [![Code Climate](https://codeclimate.com/github/47colborne/exel/badges/gpa.svg)](https://codeclimate.com/github/47colborne/exel)
4
+ [![Build Status](https://snap-ci.com/47colborne/exel/branch/master/build_image)](https://snap-ci.com/47colborne/exel/branch/master)
2
5
 
3
6
  TODO: Write a gem description
4
7
 
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
1
  require 'bundler/gem_tasks'
2
-
data/exel.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = EXEL::VERSION
9
9
  spec.authors = ['yroo']
10
10
  spec.email = ['dev@yroo.com']
11
- spec.summary = %q{EXEL, the Elastic eXEcution Language}
12
- spec.description = %q{A DSL for defining jobs that can be run in a highly scalable manner}
11
+ spec.summary = 'EXEL, the Elastic eXEcution Language'
12
+ spec.description = 'A DSL for defining jobs that can be run in a highly scalable manner'
13
13
  spec.homepage = 'https://github.com/47colborne/exel'
14
14
  spec.license = 'MIT'
15
15
 
@@ -18,14 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'aws-sdk', '~> 2'
22
- spec.add_dependency 'sidekiq', '~> 3'
23
-
24
21
  spec.add_development_dependency 'bundler', '~> 1.6'
25
22
  spec.add_development_dependency 'rake', '~> 10'
26
23
  spec.add_development_dependency 'rspec', '~> 3'
27
24
  spec.add_development_dependency 'guard', '~> 2'
28
25
  spec.add_development_dependency 'guard-rspec', '~> 4'
26
+ spec.add_development_dependency 'guard-rubocop', '~> 1'
29
27
  spec.add_development_dependency 'terminal-notifier', '~> 1'
30
28
  spec.add_development_dependency 'terminal-notifier-guard', '~> 1'
29
+ spec.add_development_dependency 'rubocop', '~> 0'
30
+ spec.add_development_dependency 'rubocop-rspec', '~> 1'
31
+ spec.add_development_dependency 'rubocop-rspec-focused', '~> 0'
31
32
  end
data/lib/exel/ast_node.rb CHANGED
@@ -2,7 +2,7 @@ module EXEL
2
2
  class ASTNode
3
3
  attr_reader :instruction, :children
4
4
 
5
- def initialize(instruction, children=[])
5
+ def initialize(instruction, children = [])
6
6
  @instruction = instruction
7
7
  @children = children
8
8
  end
@@ -12,7 +12,7 @@ module EXEL
12
12
  end
13
13
 
14
14
  def run(_context)
15
- raise "#{self.class} does not implement #process"
15
+ fail "#{self.class} does not implement #process"
16
16
  end
17
17
 
18
18
  def add_child(node)
data/lib/exel/context.rb CHANGED
@@ -4,26 +4,24 @@ module EXEL
4
4
  class Context
5
5
  attr_reader :table
6
6
 
7
- def initialize(initial_context={})
7
+ def initialize(initial_context = {})
8
8
  @table = initial_context
9
9
  end
10
10
 
11
11
  def serialize
12
- remotized_table = @table.each_with_object({}) { |(key, value), acc| acc[key] = EXEL::Resource.remotize(value) }
13
- file = serialize_context(remotized_table)
14
- upload(file)
12
+ remotized_table = @table.each_with_object({}) { |(key, value), acc| acc[key] = EXEL::Value.remotize(value) }
13
+ EXEL::Value.remotize(serialized_context(remotized_table))
15
14
  end
16
15
 
17
16
  def self.deserialize(uri)
18
- handler = Handlers::S3Handler.new
19
- file = handler.download(uri)
17
+ file = EXEL::Value.localize(uri)
20
18
  context = Marshal.load(file.read)
21
19
  file.close
22
20
  context
23
21
  end
24
22
 
25
23
  def [](key)
26
- value = EXEL::Resource.localize(@table[key])
24
+ value = EXEL::Value.localize(@table[key])
27
25
  value = get_deferred(value)
28
26
  @table[key] = value
29
27
  value
@@ -43,37 +41,36 @@ module EXEL
43
41
  end
44
42
 
45
43
  def ==(other)
46
- other.kind_of?(EXEL::Context) && table == other.table
44
+ other.is_a?(EXEL::Context) && table == other.table
45
+ end
46
+
47
+ def include?(values)
48
+ @table.merge(values) == @table
47
49
  end
48
50
 
49
51
  private
50
52
 
51
- def serialize_context(table)
53
+ def serialized_context(table)
52
54
  file = Tempfile.new(SecureRandom.uuid, encoding: 'ascii-8bit')
53
55
  file.write(Marshal.dump(Context.new(table)))
54
56
  file.rewind
55
57
  file
56
58
  end
57
59
 
58
- def upload(file)
59
- handler = Handlers::S3Handler.new
60
- handler.upload(file)
61
- end
62
-
63
60
  def get_deferred(value)
64
- if is_deferred?(value)
61
+ if deferred?(value)
65
62
  value = value.get(self)
66
- elsif value.kind_of?(Array)
63
+ elsif value.is_a?(Array)
67
64
  value.map! { |v| get_deferred(v) }
68
- elsif value.kind_of?(Hash)
65
+ elsif value.is_a?(Hash)
69
66
  value.each { |k, v| value[k] = get_deferred(v) }
70
67
  end
71
68
 
72
69
  value
73
70
  end
74
71
 
75
- def is_deferred?(value)
76
- value.kind_of?(DeferredContextValue)
72
+ def deferred?(value)
73
+ value.is_a?(DeferredContextValue)
77
74
  end
78
75
  end
79
76
  end
@@ -12,7 +12,7 @@ module EXEL
12
12
  end
13
13
 
14
14
  def get(context)
15
- keys.reduce(context) { |acc, key| acc[key] }
15
+ keys.reduce(context) { |a, e| a[e] }
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -1,8 +1,8 @@
1
1
  module EXEL
2
- class EXEL::Instruction
2
+ class Instruction
3
3
  attr_reader :name
4
4
 
5
- def initialize(name, processor_class, args, subtree=nil)
5
+ def initialize(name, processor_class, args, subtree = nil)
6
6
  @name = name
7
7
  @processor_class = processor_class
8
8
  @args = args || {}
data/lib/exel/job.rb CHANGED
@@ -2,7 +2,7 @@ module EXEL
2
2
  module Job
3
3
  class << self
4
4
  def define(job_name, &block)
5
- raise "Job #{job_name.inspect} is already defined" unless registry[job_name].nil?
5
+ fail "Job #{job_name.inspect} is already defined" unless registry[job_name].nil?
6
6
  registry[job_name] = block
7
7
  end
8
8
 
@@ -12,7 +12,7 @@ module EXEL
12
12
 
13
13
  def run(dsl_code_or_name, context = {})
14
14
  context = EXEL::Context.new(context) if context.is_a?(Hash)
15
- (ast = parse(dsl_code_or_name)) ? ast.start(context) : raise(%(Job "#{dsl_code_or_name}" not found))
15
+ (ast = parse(dsl_code_or_name)) ? ast.start(context) : fail(%(Job "#{dsl_code_or_name}" not found))
16
16
  end
17
17
 
18
18
  private
@@ -49,11 +49,11 @@ module EXEL
49
49
  add_instruction_node('process', processor_class, block, options)
50
50
  end
51
51
 
52
- def async(options={}, &block)
52
+ def async(options = {}, &block)
53
53
  add_instruction_node('async', Processors::AsyncProcessor, block, options)
54
54
  end
55
55
 
56
- def split(options={}, &block)
56
+ def split(options = {}, &block)
57
57
  add_instruction_node('split', Processors::SplitProcessor, block, options)
58
58
  end
59
59
 
@@ -63,7 +63,7 @@ module EXEL
63
63
 
64
64
  private
65
65
 
66
- def add_instruction_node(name, processor, block, args={})
66
+ def add_instruction_node(name, processor, block, args = {})
67
67
  sub_tree = block.nil? ? nil : Parser.parse(block)
68
68
  instruction = EXEL::Instruction.new(name, processor, args, sub_tree)
69
69
  node = sub_tree.nil? ? InstructionNode.new(instruction) : InstructionNode.new(instruction, [sub_tree])
data/lib/exel/logging.rb CHANGED
@@ -15,11 +15,11 @@ module EXEL
15
15
  end
16
16
 
17
17
  def self.log_filename
18
- EXEL.configuration[:log_filename] || '/dev/null'
18
+ EXEL.configuration.log_filename || '/dev/null'
19
19
  end
20
20
 
21
21
  def self.log_level
22
- level = EXEL.configuration[:log_level] || DEFAULT_LEVEL
22
+ level = EXEL.configuration.log_level || DEFAULT_LEVEL
23
23
  Logger.const_get(level.to_s.upcase)
24
24
  end
25
25
 
@@ -27,4 +27,4 @@ module EXEL
27
27
  @logger = logger || Logger.new('/dev/null')
28
28
  end
29
29
  end
30
- end
30
+ end
@@ -3,4 +3,4 @@ module EXEL
3
3
  def execute(context)
4
4
  end
5
5
  end
6
- end
6
+ end
@@ -1,6 +1,5 @@
1
1
  module EXEL
2
2
  module ProcessorHelper
3
-
4
3
  # Helper Methods
5
4
 
6
5
  def tag(*tags)
@@ -12,7 +11,7 @@ module EXEL
12
11
  end
13
12
 
14
13
  def file_size_in_mb(file)
15
- "#{'%.2f' % (file.size.to_f / 1_024_000).round(2)} MB"
14
+ format('%.2f MB', file.size.to_f / 1_024_000)
16
15
  end
17
16
 
18
17
  # Logging Helpers
@@ -38,10 +37,10 @@ module EXEL
38
37
  end
39
38
 
40
39
  def log_transaction(message = '')
41
- transaction_start_time = Time.now.to_f
40
+ transaction_start_time = Time.now
42
41
  log_info "Started at #{transaction_start_time}"
43
42
  yield(transaction_start_time)
44
- transaction_end_time = Time.now.to_f
43
+ transaction_end_time = Time.now
45
44
  log_info "Finished in #{(transaction_end_time - transaction_start_time).to_i} seconds #{message}"
46
45
  end
47
46
 
@@ -62,6 +61,5 @@ module EXEL
62
61
  time_to_sleep = duration.second.to_f - elapsed_time
63
62
  sleep(time_to_sleep) if time_to_sleep > 0
64
63
  end
65
-
66
64
  end
67
65
  end
@@ -4,18 +4,18 @@ module EXEL
4
4
  module Processors
5
5
  class AsyncProcessor
6
6
  include EXEL::ProcessorHelper
7
- attr_reader :handler
7
+ attr_reader :provider
8
8
 
9
9
  def initialize(context)
10
10
  @context = context
11
- @handler = EXEL::Handlers::SidekiqHandler.new(context)
11
+ @provider = EXEL.async_provider.new(context)
12
12
 
13
13
  log_prefix_with '[AsyncProcessor]'
14
14
  end
15
15
 
16
16
  def process(block)
17
17
  log_process do
18
- @handler.do_async(block)
18
+ @provider.do_async(block)
19
19
  log_info 'call to async completed'
20
20
  end
21
21
  end
@@ -0,0 +1,18 @@
1
+ module EXEL
2
+ module Providers
3
+ class LocalFileProvider
4
+ def upload(file)
5
+ "file://#{file.path}"
6
+ end
7
+
8
+ def download(uri)
9
+ fail 'URI must begin with "file://"' unless uri.start_with? 'file://'
10
+ File.open(uri.split('file://').last)
11
+ end
12
+
13
+ def self.remote?(uri)
14
+ uri =~ %r{file://}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module EXEL
2
+ module Providers
3
+ class ThreadedAsyncProvider
4
+ def initialize(context)
5
+ @context = context
6
+ end
7
+
8
+ def do_async(block)
9
+ Thread.new { block.start(@context) }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,11 +1,11 @@
1
1
  module EXEL
2
- class Resource
2
+ module Value
3
3
  def self.remotize(value)
4
4
  file?(value) ? upload(value) : value
5
5
  end
6
6
 
7
7
  def self.localize(value)
8
- serialized?(value) ? deserialize_file(value) : value
8
+ remote?(value) ? download(value) : value
9
9
  end
10
10
 
11
11
  class << self
@@ -15,20 +15,16 @@ module EXEL
15
15
  value.is_a?(File) || value.is_a?(Tempfile)
16
16
  end
17
17
 
18
- def serialized?(value)
19
- value =~ %r{^s3://}
18
+ def upload(file)
19
+ EXEL.remote_provider.new.upload(file)
20
20
  end
21
21
 
22
- def deserialize_file(uri)
23
- download(uri)
22
+ def remote?(value)
23
+ EXEL.remote_provider.remote?(value)
24
24
  end
25
25
 
26
26
  def download(uri)
27
- Handlers::S3Handler.new.download(uri)
28
- end
29
-
30
- def upload(file)
31
- Handlers::S3Handler.new.upload(file)
27
+ EXEL.remote_provider.new.download(uri)
32
28
  end
33
29
  end
34
30
  end
data/lib/exel/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module EXEL
2
- VERSION = '0.9.0'
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/exel.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'exel/version'
2
2
  require 'exel/logging'
3
+ require 'ostruct'
3
4
 
4
5
  module EXEL
5
6
  def self.logger
@@ -11,13 +12,21 @@ module EXEL
11
12
  end
12
13
 
13
14
  def self.configuration
14
- @config ||= {}
15
+ @config ||= OpenStruct.new
15
16
  end
16
17
 
17
18
  def self.configure
18
19
  yield configuration
19
20
  end
20
21
 
22
+ def self.async_provider
23
+ configuration.async_provider || Providers::ThreadedAsyncProvider
24
+ end
25
+
26
+ def self.remote_provider
27
+ configuration.remote_provider || Providers::LocalFileProvider
28
+ end
29
+
21
30
  root = File.expand_path('../..', __FILE__)
22
31
  Dir[File.join(root, 'lib/exel/**/*.rb')].each { |file| require file }
23
32
  end
@@ -1,23 +1,14 @@
1
1
  module EXEL
2
2
  describe ASTNode do
3
- let(:context) { {} }
4
-
5
- def build_tree
6
- @node_3 = ASTNode.new(instruction)
7
- @node_4 = ASTNode.new(instruction)
8
- @node_2 = ASTNode.new(instruction, [@node_3, @node_4])
9
- @node_5 = ASTNode.new(instruction)
10
- @node_1 = ASTNode.new(instruction, [@node_2, @node_5])
11
- end
3
+ let(:context) { instance_double(EXEL::Context) }
12
4
 
13
5
  def instruction
14
6
  instance_double(Instruction, execute: nil)
15
7
  end
16
8
 
17
- describe '#start' do
18
- class TestNode < ASTNode
19
- end
9
+ TestNode = Class.new(ASTNode)
20
10
 
11
+ describe '#start' do
21
12
  context 'when an JobTermination error bubbles up' do
22
13
  it 'should ensure the process fails silently' do
23
14
  node = TestNode.new(instruction)
@@ -29,9 +20,6 @@ module EXEL
29
20
  end
30
21
 
31
22
  describe '#run' do
32
- class TestNode < ASTNode;
33
- end
34
-
35
23
  it 'should raise an error if not implemented' do
36
24
  expect { TestNode.new(instruction).run(context) }.to raise_error 'EXEL::TestNode does not implement #process'
37
25
  end