task_rb 0.4.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 194d17f1d017541392f992a1c347ec22c04e76f1
4
+ data.tar.gz: 713adbbc091faad2991b1a18f0571968890e110a
5
+ SHA512:
6
+ metadata.gz: 2f4ba9823762f74c376c8b78883f627dab721f67c18edeb1c56f7c89793553f651c364659a12650d5fbf23b886b1ae16659c7de0201dbe148e10a2832ee5e7dd
7
+ data.tar.gz: b94c11cca50d493cb14fae48802c98467c673da301ca52d510ed2b291d26507a5e83e713bc8f4c083189ed74a0cc4719336cd1b0aad55b1d6e1f2099d1555573
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 2.1.3
5
+ - 2.2.0
6
+ - 2.3.0
7
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in task.gemspec
4
+ gemspec
5
+
6
+ gem 'cassava_rb', :git => 'git@github.com:backupify/cassava.git'
7
+ gem 'pyper_rb', :git => 'git@github.com:backupify/pyper.git'
8
+ gem 'values', :git => 'git@github.com:backupify/values.git'
9
+
10
+ group :development, :test do
11
+ gem "pry"
12
+ gem "awesome_print"
13
+ gem 'm', :git => 'git@github.com:ANorwell/m.git', :branch => 'minitest_5'
14
+ end
15
+
16
+ group :test do
17
+ gem 'minitest_should', :git => 'git@github.com:citrus/minitest_should.git'
18
+ gem "mocha"
19
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Datto
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.
@@ -0,0 +1,154 @@
1
+ # Task
2
+
3
+ [![Build Status](https://travis-ci.org/backupify/task.svg?branch=master)](https://travis-ci.org/backupify/task)
4
+
5
+ Task provides a toolbox for generating, tracking and serializing tasks to be performed.
6
+ It is NOT a job queuing framework, a la resque or qless, and does not provide ways to execute tasks
7
+ in a distributed fashion. Rather, Task could be used with such a framework to provide "at-least-once"
8
+ task execution guarantees, or to perform multiple, lightweight tasks within a single job.
9
+
10
+ An example task lifecycle could be follows:
11
+
12
+ 1. A task is generated by a task generator process
13
+ 2. The task is saved to a backing data store, recording that it has been generated and should be completed.
14
+ 3. The task is passed, in serialized format, directly from the generating process to another worker process
15
+ or host to complete.
16
+ 4. The worker process begins working on the task, and:
17
+ * The worker process completes the task, marking it as completed in the backing data store.
18
+
19
+ OR
20
+
21
+ * The worker process fails to complete the task.
22
+ * A subsequent worker can fetch the task from the backing data store and attempts to complete it.
23
+
24
+ Task provides mechanisms creating tasks, saving and loading tasks from the backend datastore, and serializing tasks
25
+ for transfer between processes or hosts. However, Task is not a framework, and each step in the lifecycle
26
+ above would be implemented in application code.
27
+
28
+ Out of the box, Task uses Cassandra as the backing data store, but other backends could be implemented.
29
+ Cassandra provides stronger durability guarantees than some other data stores (for example, Redis). Additionally,
30
+ Cassandra prefers write-heavy workloads. In the workload described above, A task need only be read from the
31
+ datastore if it fails to complete initially. In situations where most tasks complete on the first attempt,
32
+ the majority of datastore operations will be writes. However, because Task does not enforce a usage pattern,
33
+ this could be usage-dependent.
34
+
35
+ ## Installation
36
+
37
+ Add this line to your application's Gemfile:
38
+
39
+ ```ruby
40
+ gem 'task_rb', github: 'backupify/task'
41
+ ```
42
+
43
+ And then execute:
44
+
45
+ $ bundle
46
+
47
+ ## Usage
48
+
49
+ ### Defining and Creating a Task
50
+
51
+ To define a task, mix in the Task module:
52
+
53
+ ```ruby
54
+ class FetchFile
55
+ include Task::Task
56
+
57
+ data_attr_reader :external_host
58
+ data_attr_reader :filename
59
+ end
60
+ ```
61
+
62
+ A task contains a set of data key-value pairs; The `data_attr_reader` helper provides accessors for various expected
63
+ data fields, but is not required.
64
+
65
+ To build a task, we invoke the build method:
66
+
67
+ ```ruby
68
+ file_task = FetchFile.build(
69
+ id: 'file1',
70
+ task_list: 'datto.com',
71
+ external_host: 'datto.com',
72
+ filename: 'file1.txt')
73
+ ```
74
+
75
+ Here, the task belongs to the 'datto.com' task list. The ID of the task should be unique across the task list (if one
76
+ is not specified, a UUID will be used). Other provided fields just become part of the data of task; this is just syntactic
77
+ sugar for:
78
+
79
+ ```ruby
80
+ file_task = FetchFile.new(
81
+ id: 'file1',
82
+ task_list: 'datto.com',
83
+ data: { external_host: 'datto.com', filename: 'file1.txt' })
84
+ ```
85
+
86
+ On the task object, the `data_attr_readers` allow access to data fields, so `file_task.data[:external_host]` and
87
+ `file_task.external_host` are equivalent. This is just a convenience, and does not enforce any restrictions on which
88
+ fields can and cannot be placed in the task data.
89
+
90
+ ### Saving and Loading Tasks from the Datastore
91
+
92
+ To save to the datastore:
93
+
94
+ ```ruby
95
+ file_task.save
96
+ ```
97
+
98
+ If an existing task with the same task list and ID already exists within the backend store, it will be overwritten.
99
+
100
+ To fetch a particular task (by id) from the datastore:
101
+
102
+ ```ruby
103
+ file_task = Task::Task.find(task_list, id)
104
+ ```
105
+
106
+ To fetch all tasks for a task list from the datastore:
107
+
108
+ ```ruby
109
+ tasks_enumerator = Task::Task.all(task_list)
110
+ ```
111
+
112
+ ### Serializing and Deserializing Tasks
113
+
114
+ To serialize as a hash:
115
+
116
+ ```ruby
117
+ task_hash = my_task.as_hash
118
+ ```
119
+
120
+ To deserialize a task hash to the task that was originally serialized:
121
+
122
+ ```ruby
123
+ my_task = Task::Task.from_hash(task_hash)
124
+ ```
125
+
126
+ Task does not enforce a particular over the wire serialization format.
127
+
128
+ ### Configuring the Datastore
129
+
130
+ The datastore interface is backed by an adapter. Which adapter is used, and how it is
131
+ constructed, is configurable, by specifying the adapter builder. The specified lambda function
132
+ should take the options passed to the `Interface#new` method, and return the adapter instance:
133
+
134
+ ```ruby
135
+ Task::DataInterface::Interface.adapter_builder = ->(_options) do
136
+ session = Cassandra.cluster(port: 1234, hosts: ['my_host']).connect('my_tasks')
137
+ CassandraAdapter.new(client: Cassava::Client.new(session))
138
+ end
139
+ ```
140
+
141
+ This example configures the CassandraAdapter to connect to Cassandra on port 1234 and host 'my_host', and to
142
+ use the keyspace 'my_tasks'. Similarly, a completely separate adapter could be used.
143
+
144
+ Additionally, data interface instances can be constructed by passing in an adapter, allowing different
145
+ adapters (or adapter configurations) to be used at once.
146
+
147
+ ## Contributing
148
+
149
+ Bug reports and pull requests are welcome on GitHub at https://github.com/backupify/task.
150
+
151
+
152
+ ## License
153
+
154
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -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 => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "task"
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
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ require "task/version"
2
+ require_relative 'task/data_interface'
3
+ require_relative 'task/task'
4
+ require_relative 'task/tasks'
5
+ require_relative 'task/generators'
6
+
7
+ module Task; end
@@ -0,0 +1,3 @@
1
+ module Task::DataInterface; end
2
+ require_relative 'data_interface/cassandra_adapter'
3
+ require_relative 'data_interface/interface'
@@ -0,0 +1,68 @@
1
+ require 'pyper/all'
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module Task::DataInterface
6
+ class CassandraAdapter
7
+ attr_reader :client, :tasks_table_name
8
+
9
+ # @option opts [Cassava::Client] :client The Cassandra Client to use
10
+ # @option opts [Symbol] :tasks_table_name The table name of the cassandra table containing the tasks
11
+ # Defaults to :tasks
12
+ def initialize(opts = {})
13
+ @client = opts[:client]
14
+ @tasks_table_name = opts[:tasks_table_name] || :tasks
15
+ end
16
+
17
+ # (see Interface)
18
+ def store(task)
19
+ pipeline = Pyper::Pipeline.new
20
+
21
+ # Serialize the attributes to be stored
22
+ pipeline << Pyper::Pipes::Model::AttributeSerializer.new
23
+
24
+ # Store the serialized attributes in the tasks table
25
+ pipeline << Pyper::Pipes::Cassandra::Writer.new(tasks_table_name, client)
26
+ pipeline.push(task.as_hash)
27
+ end
28
+
29
+ # (see Interface)
30
+ def delete(task_list, task_id)
31
+ client.delete(:tasks).where(:task_list => task_list, :id => task_id).execute
32
+ end
33
+
34
+ # (see Interface)
35
+ def all(task_list)
36
+ read_pipe.push(:task_list => task_list).value
37
+ end
38
+
39
+ def find(task_list, task_id)
40
+ read_pipe.push(:task_list => task_list, :id => task_id).value.first
41
+ end
42
+
43
+ private
44
+
45
+ def read_pipe
46
+ pipeline = Pyper::Pipeline.new
47
+
48
+ # Read items from cassandra, as determined by the args pushed into the pipeline
49
+ pipeline << Pyper::Pipes::Cassandra::Reader.new(tasks_table_name, client)
50
+
51
+ # Deserialize the data field into a hash
52
+ pipeline << Pyper::Pipes::Model::AttributeDeserializer.new('data' => Hash)
53
+
54
+ # Deserialize items into Task objects
55
+ pipeline << TaskDeserializer
56
+
57
+ pipeline
58
+ end
59
+
60
+ class TaskDeserializer
61
+ def self.pipe(items, status)
62
+ items.map do |item|
63
+ Task::Task.from_hash(item.with_indifferent_access)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,53 @@
1
+ require 'cassava'
2
+ require 'active_support/core_ext/class/attribute'
3
+ require_relative 'cassandra_adapter'
4
+
5
+ module Task::DataInterface
6
+ class Interface
7
+ attr_reader :adapter
8
+
9
+ # The adapter builder function used to initialize the adapter based on the options provided to new.
10
+ # By default, a CassandraAdapter will be used. For this default adapter builder, the following
11
+ # arguments are supported:
12
+ # * :keyspace - the keyspace to use; default is 'tasks'
13
+ # * any arguments accepted by the #Cassandra.cluster method, including :port and :hosts
14
+ class_attribute :adapter_builder
15
+ self.adapter_builder = ->(options) do
16
+ session = Cassandra.cluster(options).connect(options[:keyspace] || 'tasks')
17
+ CassandraAdapter.new(client: Cassava::Client.new(session))
18
+ end
19
+
20
+ # @option options [#store,#all,#find,#delete] :adapter adapter to use.
21
+ # Otherwise, the configured adapter builder will be used
22
+ def initialize(options = {})
23
+ @adapter = options[:adapter] || self.class.adapter_builder.call(options)
24
+ end
25
+ # Stores a task in the data store
26
+ # @param [Task::Task] task
27
+ def store(task)
28
+ adapter.store(task)
29
+ end
30
+
31
+ # Returns all tasks for the provided task list
32
+ # @param [String] task_list
33
+ # @return [Enumerator::Lazy<Task::Task>]
34
+ def all(task_list)
35
+ adapter.all(task_list)
36
+ end
37
+
38
+ # Returns the task with the given id
39
+ # @param [String] task_list
40
+ # @param [String] task_id
41
+ # @return [Task::Task|NilClass]
42
+ def find(task_list, task_id)
43
+ adapter.find(task_list, task_id)
44
+ end
45
+
46
+ # Deletes the task with the given id.
47
+ # @param [String] task_list
48
+ # @param [String] task_id
49
+ def delete(task_list, task_id)
50
+ adapter.delete(task_list, task_id)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ require 'values'
2
+
3
+ module Task
4
+ module Generator
5
+ include ::Enumerable
6
+
7
+ def append(*generators)
8
+ Generators::CombinedGenerator.new([self] + generators)
9
+ end
10
+ end
11
+
12
+ module Generators
13
+ class CombinedGenerator < Value.new(:generators)
14
+ include Generator
15
+
16
+ def each
17
+ generators.each do |generator|
18
+ generator.each { |t| yield t }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'generator'
2
+ require_relative 'generators/existing_tasks'
@@ -0,0 +1,12 @@
1
+ require 'values'
2
+ require_relative '../generator'
3
+
4
+ module Task::Generators
5
+ class ExistingTasks < Value.new(:task_list)
6
+ include ::Task::Generator
7
+
8
+ def each
9
+ Task::Task.all(task_list).each { |t| yield t }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,162 @@
1
+ require 'virtus'
2
+ require 'active_support/concern'
3
+ require_relative 'data_interface/interface'
4
+
5
+ module Task
6
+ # A Task represents a task to be performed. The task can be serialized as JSON to be passed to
7
+ # other processes to complete, and can be stored in Cassandra as a means of ensuring "at-least-once"
8
+ # task completion for this task. So, an example lifecycle of a task would be:
9
+ # 1) A task is generated in a "master" job
10
+ # 2) The master job saves the task, providing a record that the task was generated.
11
+ # 3) The master job passes the task to another job to complete the task.
12
+ # 4) The worker job completes task, removing the record for that task.
13
+ # 5) If the worker job fails, the task is not completed and so the record for it persists.
14
+ # 6) A subsequent job can fetch the list of tasks, returning the tasks that failed to complete.
15
+ # This job could either serialize them to be completed by other jobs, or complete them directly.
16
+ #
17
+ # A task has a unique id, belongs to a task list that group similar tasks together, and has data
18
+ # associated with it.
19
+ #
20
+ # @example Defining a type of Task
21
+ # class MyDeleteTask
22
+ # include Task::Task
23
+ # # This task has the item_id data field, representing the item to delete
24
+ # data_attr_reader :item_id
25
+ # end
26
+ #
27
+ # @example Creating a task
28
+ # my_task = MyDeleteTask.build(:id => 'my_id', :task_list => "#{service.id}-delete", :item_id => '123')
29
+ #
30
+ # @example Serializing and deserializing a task
31
+ # # In the process that generated the task
32
+ # serialized = my_task.as_hash
33
+ #
34
+ # # In the process that acts on the hash
35
+ # my_task_copy = Task::Task.from_hash(serialized)
36
+ #
37
+ # @example Saving a task
38
+ # my_task.save
39
+ #
40
+ # @example Fetching a single task that has been saved
41
+ # Task::DataInterface::Interface.new.find(task_list, task_id)
42
+ #
43
+ # @example Fetching all tasks for a task list
44
+ # Task::DataInterface::Interface.new.all(task_list)
45
+ #
46
+ # @example Completing a task, so that it is not longer fetchable
47
+ # my_task.complete
48
+ #
49
+ module Task
50
+ extend ActiveSupport::Concern
51
+ include Virtus.module
52
+
53
+ attribute :task_list, String
54
+ attribute :id, String
55
+ attribute :data, Hash[Symbol => Object]
56
+
57
+ module ClassMethods
58
+ # Instantiate an instance of this Task subclass.
59
+ # @param options [Hash] Options to instantiate this Task. :task_list and :id are required;
60
+ # other arguments will be passed as data to the task.
61
+ # @option options [String] :task_list
62
+ # @option options [String] :id
63
+ def build(options)
64
+ task_list = options.delete(:task_list)
65
+ id = options.delete(:id) || SecureRandom.hex
66
+ new(task_list: task_list, id: id, data: options)
67
+ end
68
+
69
+ # Instantiate an instance of this Task subclass and save it to the datastore.
70
+ # @param options [Hash] Options to instantiate this Task. :task_list and :id are required;
71
+ # other arguments will be passed as data to the task.
72
+ # @option options [String] :task_list
73
+ # @option options [String] :id
74
+ def create(options)
75
+ task = build(options)
76
+ task.save
77
+ task
78
+ end
79
+
80
+ # Defines an attr reader instance method for a field in the data hash.
81
+ #
82
+ # @example
83
+ # class MyTask
84
+ # include Task::Task
85
+ # data_attr_reader :my_data_field
86
+ # end
87
+ #
88
+ # @param attr_name [Symbol] The attr name of the data field which will be used.
89
+ def data_attr_reader(attr_name)
90
+ define_method(attr_name) { data[attr_name] }
91
+ end
92
+ end
93
+
94
+ # Creates a Task object from the provided hash. Generally, this task object should NOT be
95
+ # constructed manually using this method. Rather, this provides a way to reconstitute a task
96
+ # that was previously serialized using the #as_hash method.
97
+ #
98
+ # @example
99
+ # class MyTask
100
+ # include Task::Task
101
+ # data_attr_reader :my_data_field
102
+ # end
103
+ # my_task = MyTask.new(:task_list => 'my_task_list', )
104
+
105
+ #
106
+ # @param task_hash [Hash] Should contain the :task_list, :id, :type, and :data fields
107
+ # @return [Task::Task] The task object.
108
+ def self.from_hash(task_hash)
109
+ task_hash = task_hash.dup
110
+ type = task_hash.delete(:type)
111
+ type.constantize.new(task_hash)
112
+ end
113
+
114
+ # Returns the task with the given id
115
+ # @param [String] task_list
116
+ # @param [String] task_id
117
+ # @return [Task::Task|NilClass]
118
+ def self.find(task_list, id)
119
+ interface.find(task_list, id)
120
+ end
121
+
122
+ # Returns all tasks for the provided task list
123
+ # @param [String] task_list
124
+ # @return [Enumerator::Lazy<Task::Task>]
125
+ def self.all(task_list)
126
+ interface.all(task_list)
127
+ end
128
+
129
+ # The Data Interface used
130
+ # @return [Task::DataInterface::Interface]
131
+ def self.interface
132
+ DataInterface::Interface.new
133
+ end
134
+
135
+ # Executes this task
136
+ # @param options [Hash] Options specific to the execution of this task
137
+ def execute(options = {})
138
+ raise NotImplementedError.new('execute method not implemented')
139
+ end
140
+
141
+ # Serialized this Task as a hash
142
+ # @return [Hash]
143
+ def as_hash
144
+ attributes.merge(type: self.class.to_s)
145
+ end
146
+
147
+ # Saves this task to the data store.
148
+ # @return [NilClass]
149
+ def save
150
+ Task.interface.store(self)
151
+ nil
152
+ end
153
+
154
+
155
+ # Marks this task as complete, removing it from the datastore
156
+ # @return [NilClass]
157
+ def complete
158
+ Task.interface.delete(task_list, id)
159
+ nil
160
+ end
161
+ end
162
+ end
@@ -0,0 +1 @@
1
+ require_relative 'tasks/composite_task.rb'
@@ -0,0 +1,19 @@
1
+ require_relative '../task'
2
+
3
+ module Task::Tasks
4
+ class CompositeTask
5
+ include Task::Task
6
+
7
+ data_attr_reader :child_task_list
8
+
9
+ # @param opts [Hash] Options to pass to the execute method of each child task
10
+ # @return [Array] The sequence of return values from each task execution
11
+ def execute(opts = {})
12
+ (Task::Task.all(child_task_list).map do |task|
13
+ task_result = task.execute(opts)
14
+ task.complete
15
+ task_result
16
+ end).force
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Task
2
+ VERSION = "0.4.0"
3
+ end
@@ -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 'task/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "task_rb"
8
+ spec.version = Task::VERSION
9
+ spec.authors = ["Arron Norwell"]
10
+ spec.email = ["anorwell@datto.com"]
11
+
12
+ spec.summary = %q{Task provides a toolbox for generating, tracking and serializing tasks to be performed.}
13
+ spec.homepage = "http://datto.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "minitest-reporters"
25
+
26
+ spec.add_dependency 'pyper_rb', '~> 1.0'
27
+ spec.add_dependency 'cassava_rb'
28
+ spec.add_dependency 'virtus'
29
+ spec.add_dependency 'values'
30
+ spec.add_dependency "activesupport"
31
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: task_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Arron Norwell
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
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: pyper_rb
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: cassava_rb
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: virtus
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: values
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activesupport
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description:
140
+ email:
141
+ - anorwell@datto.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".travis.yml"
148
+ - Gemfile
149
+ - LICENSE.txt
150
+ - README.md
151
+ - Rakefile
152
+ - bin/console
153
+ - bin/setup
154
+ - lib/task.rb
155
+ - lib/task/data_interface.rb
156
+ - lib/task/data_interface/cassandra_adapter.rb
157
+ - lib/task/data_interface/interface.rb
158
+ - lib/task/generator.rb
159
+ - lib/task/generators.rb
160
+ - lib/task/generators/existing_tasks.rb
161
+ - lib/task/task.rb
162
+ - lib/task/tasks.rb
163
+ - lib/task/tasks/composite_task.rb
164
+ - lib/task/version.rb
165
+ - task_rb.gemspec
166
+ homepage: http://datto.com
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.4.8
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Task provides a toolbox for generating, tracking and serializing tasks to
190
+ be performed.
191
+ test_files: []