iron_response 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Code Climate](https://codeclimate.com/github/adelevie/iron_response.png)](https://codeclimate.com/github/adelevie/iron_response) [![Gem Version](https://badge.fury.io/rb/iron_response.png)](http://badge.fury.io/rb/iron_response)
2
+
1
3
  # iron_response
2
4
 
3
5
  Provides a response object for IronWorkers.
@@ -5,40 +7,140 @@ Provides a response object for IronWorkers.
5
7
  ```ruby
6
8
  require "iron_response"
7
9
 
10
+ config = {...}
8
11
  batch = IronResponse::Batch.new
9
- batch.config[:aws_s3] = {
10
- access_key_id: 123,
11
- secret_access_key: 456,
12
- bucket: "iron_worker"
13
- }
14
- batch.config[:iron_io] = {
15
- project_id: "abc",
16
- token: "defg"
17
- }
18
- batch.worker = "path/to/worker/is_prime"
19
- batch.params_array = [1..1000].map {|i| {number: i}}
20
- results = batch.run!
12
+
13
+ batch.auto_update_worker = true
14
+ batch.config[:iron_io] = config[:iron_io]
15
+ batch.config[:aws_s3] = config[:aws_s3]
16
+ batch.worker = "test/workers/is_prime.rb"
17
+ batch.params_array = Array(1..10).map {|i| {number: i}}
18
+
19
+ results = batch.run!
21
20
 
22
21
  p results
23
- #=> [true, true, true, false, true, false...]
22
+ #=> [{"result"=>false}, {"result"=>true}, {"result"=>true}...]
24
23
  ```
25
24
 
26
25
  Assumes you have a worker file called `is_prime.rb`:
27
26
  ```ruby
28
27
  require "iron_response"
29
28
 
30
- IronResponse::Responder do
29
+ IronResponse::Responder.new(binding) do
31
30
  def is_prime?(n)
32
31
  ("1" * n =~ /^1?$|^(11+?)\1+$/) == 0 ? false : true
33
32
  end
34
33
 
35
- is_prime? params[:number]
34
+ {
35
+ result: is_prime?(params[:number])
36
+ }
36
37
  end
37
38
  ```
38
39
 
39
- ## Installation
40
+ ## Rationale
41
+
42
+ Iron.io's IronWorker is a great product that provides a lot of powerful concurrency options. With IronWorker, you can scale tasks to hundreds and even thousands of workers. However, IronWorker was missing one useful feature for me: responses. What do I mean? In the typical IronWorker setup, worker files are just one-off scripts that run independently of the client that queues them up. For example:
43
+
44
+ ```ruby
45
+ client = IronWorkerNG::Client.new
46
+ 100.times do |i|
47
+ client.tasks.create("do_something", number: i)
48
+ end
49
+ ```
50
+
51
+ For many use cases, this is fine. But what if I want to know the result of `do_something`? A simple way to get the result would be for your worker to POST the final result somewhere, then have the client it. This gem simply abstracts that process away, allowing the developer to avoid boilerplate and to keep worker code elegant.
52
+
53
+ Under the hood, `iron_response` uses some functional and meta-programming to capture the final expression of a worker file, convert it to JSON, and then POST it to Amazon S3. When all the workers in an `IronResponse::Batch` have finished, the gem retrieves the file and converts the JSON string back to Ruby.
40
54
 
41
- Don't install this yet. It's just a code sketch so far. Once this library works, these will be the installation instructions:
55
+ This process means there a few important implications:
56
+
57
+ - Response objects "sent" from workers should be JSON-parseable. This means sticking to basic Ruby objects and data structures such as `String`, `Fixnum`, `Hash`, and `Array`.
58
+ - Though these objects must be parseable, they can be fairly large. Though I haven't figured out exactly what the limit is, you really are only constrained by IronWorkers RAM and HD limits, and the bandwidth between the client (your computer/server), IronWorker servers, and S3. In an ideal setup, all three of these components are inside of Amazon's cloud, allowing you to get very fast throughput.
59
+
60
+ One final gotcha: I haven't yet implemented a clean way to use the `iron_worker_ng` gem to declare dependencies. This should be done soon, though.
61
+
62
+ ## Usage
63
+
64
+ This gem requires a basic understanding of how to use [IronWorker with Ruby](https://github.com/iron-io/iron_worker_ruby_ng).
65
+
66
+ Assuming you have an empy directory, called `foo`:
67
+
68
+ ```sh
69
+ $ mkdir workers
70
+ $ cd workers
71
+ $ touch my_worker.rb
72
+ ```
73
+
74
+ `my_worker.rb` should look like this:
75
+
76
+ ```ruby
77
+ require "iron_response"
78
+
79
+ IronResponse::Responder.new(binding) do
80
+ # your code here
81
+ end
82
+ ```
83
+
84
+ To run this worker, create at the top-level of `foo` files called `configuration.rb` and `enqueue.rb`:
85
+
86
+ `configuration.rb`:
87
+ ```ruby
88
+ class Configuration
89
+ def self.keys
90
+ {
91
+ iron_io: {
92
+ token: "123",
93
+ project_id: "123"
94
+ },
95
+ aws_s3: {
96
+ access_key_id: "123",
97
+ secret_access_key: "123",
98
+ bucket: "iron_response"
99
+ }
100
+ }
101
+ end
102
+ end
103
+ ```
104
+
105
+ Obviously, fill in the appropriate API keys. It is highly recommended that you do not use your AWS master keys. Instead, go to the AWS Console, click on "IAM", and create a user with a policy that allows it to edit the bucket named in the configuration file. Here's an example policy:
106
+
107
+ ```json
108
+ {
109
+ "Statement": [
110
+ {
111
+ "Action": "s3:*",
112
+ "Effect": "Allow",
113
+ "Resource": [
114
+ "arn:aws:s3:::iron_response",
115
+ "arn:aws:s3:::iron_response/*"
116
+ ]
117
+ }
118
+ ]
119
+ }
120
+ ```
121
+
122
+ Now, write your queueing script:
123
+
124
+ `enqueue.rb`:
125
+ ```ruby
126
+ require_relative "configuration"
127
+ require "iron_response"
128
+
129
+ config = Configuration.keys
130
+ batch = IronResponse::Batch.new
131
+
132
+ batch.auto_update_worker = true
133
+ batch.config = config
134
+ batch.worker = "workers/my_worker.rb"
135
+
136
+ # The `params_array` is an Array of Hashes
137
+ # that get sent as the payload to IronWorker scripts.
138
+ batch.params_array = Array ("a".."z").map {|i| {letter: i}}
139
+
140
+ results = batch.run!
141
+ ```
142
+
143
+ ## Installation
42
144
 
43
145
  Add this line to your application's Gemfile:
44
146
 
@@ -16,6 +16,8 @@ Gem::Specification.new do |gem|
16
16
  gem.version = IronResponse::VERSION
17
17
 
18
18
  gem.add_dependency "iron_worker_ng"
19
+ gem.add_dependency "aws-s3"
19
20
  gem.add_development_dependency "minitest", "~> 4.7.3"
20
21
  gem.add_development_dependency "pry"
22
+ gem.add_development_dependency "pry-rescue"
21
23
  end
@@ -1,3 +1,3 @@
1
1
  module IronResponse
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/iron_response.rb CHANGED
@@ -1,14 +1,31 @@
1
1
  require "iron_response/version"
2
2
  require "iron_worker_ng"
3
+ require "aws/s3"
4
+ require "json"
3
5
 
4
6
  module IronResponse
7
+ module Protocol
8
+ S3_PATH = "tasks"
9
+
10
+ def Protocol.s3_path(task_id)
11
+ "#{S3_PATH}/#{task_id}.json"
12
+ end
13
+ end
14
+
5
15
  class Responder
6
- def initialize(&block)
7
- send_data_to_s3(block.call)
16
+ def initialize(binding, &block)
17
+ task_id = eval("@iron_task_id", binding)
18
+ params = eval("params", binding)
19
+ send_data_to_s3(params, task_id, block.call)
8
20
  end
9
21
 
10
- def send_data_to_s3(data)
11
- p "pretending to send #{data} to s3"
22
+ def send_data_to_s3(params, task_id, data)
23
+ aws_s3 = params[:aws_s3]
24
+ AWS::S3::Base.establish_connection! access_key_id: aws_s3[:access_key_id],
25
+ secret_access_key: aws_s3[:secret_access_key]
26
+ path = IronResponse::Protocol.s3_path(task_id)
27
+ bucket_name = params[:aws_s3][:bucket]
28
+ AWS::S3::S3Object.store(path, data.to_json, bucket_name)
12
29
  end
13
30
  end
14
31
 
@@ -23,7 +40,7 @@ module IronResponse
23
40
  end
24
41
 
25
42
  def worker_name
26
- @worker.split("/").last
43
+ @worker.split("/").last.split(".rb").first
27
44
  end
28
45
 
29
46
  def run!
@@ -33,18 +50,27 @@ module IronResponse
33
50
  create_code!
34
51
  end
35
52
 
36
- pids = params_array.map do |params|
53
+ task_ids = params_array.map do |params|
37
54
  params[:aws_s3] = @config[:aws_s3]
38
55
  @client.tasks.create(worker_name, params)._id
39
56
  end
40
57
 
41
- pids.map do |pid|
42
- get_response_from_pid(@client.tasks.wait_for(pid))
58
+ task_ids.map do |task_id|
59
+ get_response_from_task_id(@client.tasks.wait_for(task_id)._id)
43
60
  end
44
61
  end
45
62
 
46
- def get_response_from_pid(pid)
47
- "FakeResponseFromPid:#{pid}"
63
+ def get_response_from_task_id(task_id)
64
+ aws_s3 = @config[:aws_s3]
65
+ AWS::S3::Base.establish_connection! access_key_id: aws_s3[:access_key_id],
66
+ secret_access_key: aws_s3[:secret_access_key]
67
+
68
+ bucket_name = @config[:aws_s3][:bucket]
69
+ bucket = AWS::S3::Bucket.find(bucket_name)
70
+ path = IronResponse::Protocol.s3_path(task_id)
71
+ response = bucket[path].value
72
+
73
+ JSON.parse(response)
48
74
  end
49
75
 
50
76
  def create_code!
@@ -1,7 +1,7 @@
1
1
  require "test_helper"
2
2
 
3
- class RddTest < MiniTest::Unit::TestCase
4
- def test_readme
3
+ class SynopsisTest < MiniTest::Unit::TestCase
4
+ def test_synopsis
5
5
 
6
6
  config = Configuration.keys
7
7
  batch = IronResponse::Batch.new
@@ -10,11 +10,11 @@ class RddTest < MiniTest::Unit::TestCase
10
10
  batch.config[:iron_io] = config[:iron_io]
11
11
  batch.config[:aws_s3] = config[:aws_s3]
12
12
  batch.worker = "test/workers/is_prime.rb"
13
- batch.params_array = Array(1..10).map {|i| {number: i}}
13
+ batch.params_array = Array(1..20).map {|i| {number: i}}
14
14
 
15
15
  results = batch.run!
16
16
 
17
- p results
18
17
  assert_equal Array, results.class
18
+ assert_equal batch.params_array.length, results.length
19
19
  end
20
20
  end
data/test/test_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require_relative "configuration"
2
2
  require "minitest/unit"
3
3
  require "minitest/autorun"
4
+ require "pry-rescue/minitest"
4
5
  require "minitest/pride"
5
6
  require "pry"
6
7
  require "iron_response"
@@ -1,12 +1,11 @@
1
1
  require "iron_response"
2
2
 
3
- IronResponse::Responder.new do
3
+ IronResponse::Responder.new(binding) do
4
4
  def is_prime?(n)
5
5
  ("1" * n =~ /^1?$|^(11+?)\1+$/) == 0 ? false : true
6
6
  end
7
7
 
8
- result = is_prime?(params[:number])
9
- p result
10
-
11
- return result
8
+ {
9
+ result: is_prime?(params[:number])
10
+ }
12
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iron_response
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: aws-s3
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: minitest
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +75,22 @@ dependencies:
59
75
  - - ! '>='
60
76
  - !ruby/object:Gem::Version
61
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry-rescue
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
62
94
  description: Provides a response object for IronWorker tasks.
63
95
  email:
64
96
  - adelevie@gmail.com
@@ -75,7 +107,7 @@ files:
75
107
  - lib/iron_response.rb
76
108
  - lib/iron_response/version.rb
77
109
  - test/configuration.rb.example
78
- - test/rdd_test.rb
110
+ - test/synopsis_test.rb
79
111
  - test/test_helper.rb
80
112
  - test/workers/is_prime.rb
81
113
  homepage: ''
@@ -104,7 +136,7 @@ specification_version: 3
104
136
  summary: Provides a response object for IronWorker tasks.
105
137
  test_files:
106
138
  - test/configuration.rb.example
107
- - test/rdd_test.rb
139
+ - test/synopsis_test.rb
108
140
  - test/test_helper.rb
109
141
  - test/workers/is_prime.rb
110
142
  has_rdoc: