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 +119 -17
- data/iron_response.gemspec +2 -0
- data/lib/iron_response/version.rb +1 -1
- data/lib/iron_response.rb +36 -10
- data/test/{rdd_test.rb → synopsis_test.rb} +4 -4
- data/test/test_helper.rb +1 -0
- data/test/workers/is_prime.rb +4 -5
- metadata +35 -3
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
batch.
|
15
|
-
|
16
|
-
|
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
|
-
#=> [
|
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
|
-
|
34
|
+
{
|
35
|
+
result: is_prime?(params[:number])
|
36
|
+
}
|
36
37
|
end
|
37
38
|
```
|
38
39
|
|
39
|
-
##
|
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
|
-
|
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
|
|
data/iron_response.gemspec
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
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
|
47
|
-
|
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
|
4
|
-
def
|
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..
|
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
data/test/workers/is_prime.rb
CHANGED
@@ -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
|
-
|
9
|
-
|
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.
|
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/
|
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/
|
139
|
+
- test/synopsis_test.rb
|
108
140
|
- test/test_helper.rb
|
109
141
|
- test/workers/is_prime.rb
|
110
142
|
has_rdoc:
|