alephant 0.0.4-java → 0.0.5-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/README.md +64 -4
- data/lib/alephant.rb +63 -18
- data/lib/alephant/models/cache.rb +11 -7
- data/lib/alephant/models/queue.rb +25 -0
- data/lib/alephant/models/sequencer.rb +40 -23
- data/lib/alephant/version.rb +1 -1
- data/spec/alephant_spec.rb +193 -0
- data/spec/cache_spec.rb +63 -0
- data/spec/queue_spec.rb +78 -0
- data/spec/sequencer_spec.rb +121 -0
- data/spec/spec_helper.rb +1 -0
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fab1ace0ff9d2ef8b5bb2003bb6fced04808a48
|
4
|
+
data.tar.gz: fd3b8c6a161ea075fef0865fbb1278cb5ac4c47c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d72470cccb094e58d2735fbce0b19fbf2840949f2ba3aabfd97bd0c400fe8b43baf1da54577677d4c006d972ed49808eef25f9400f4c52641f4df5b05880a345
|
7
|
+
data.tar.gz: 35b38982cfc40c7cf796c2b440d288a6373b0e948f54d85e63e0f4ed8bb806dd9d7c8ea9e3971eb780579dda240b69d3f3fc55378daa42c0fe53df06b4d867a5
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
jruby-1.7.
|
1
|
+
jruby-1.7.9
|
data/README.md
CHANGED
@@ -3,16 +3,19 @@ alephant
|
|
3
3
|
|
4
4
|
Static publishing to S3 on push notification from SQS
|
5
5
|
|
6
|
-
[![Code Climate](https://codeclimate.com/repos/
|
6
|
+
[![Code Climate](https://codeclimate.com/repos/52d6bec56956802e26011a0f/badges/fce457795179641460e0/gpa.png)](https://codeclimate.com/repos/52d6bec56956802e26011a0f/feed)
|
7
7
|
|
8
|
-
[![Build Status](https://travis-ci.org/
|
8
|
+
[![Build Status](https://travis-ci.org/BBC-News/alephant.png?branch=master)](https://travis-ci.org/BBC-News/alephant)
|
9
9
|
|
10
10
|
[![Gem Version](https://badge.fury.io/rb/alephant.png)](http://badge.fury.io/rb/alephant)
|
11
11
|
|
12
12
|
##Dependencies
|
13
13
|
|
14
14
|
- JRuby 1.7.8
|
15
|
-
- An AWS account
|
15
|
+
- An AWS account (you'll need to create):
|
16
|
+
- An S3 bucket
|
17
|
+
- An SQS Queue
|
18
|
+
- A Dynamo DB table (optional, will attempt to create if can't be found)
|
16
19
|
|
17
20
|
##Setup
|
18
21
|
|
@@ -30,7 +33,64 @@ gem install alephant
|
|
30
33
|
In your application:
|
31
34
|
```rb
|
32
35
|
require 'alephant'
|
36
|
+
opts = {
|
37
|
+
:s3_bucket_id => 'bucket-id',
|
38
|
+
:s3_object_path => 'path/to/object',
|
39
|
+
:s3_object_id => 'object_id',
|
40
|
+
:table_name => 'your_dynamo_db_table',
|
41
|
+
:sqs_queue_id => 'https://your_amazon_sqs_queue_url',
|
42
|
+
:view_id => 'my_view_id',
|
43
|
+
:sequential_proc => Proc.new { |last_seen_id, data|
|
44
|
+
last_seen_id < data["sequence_id"].to_i
|
45
|
+
},
|
46
|
+
:set_last_seen_proc => Proc.new { |data|
|
47
|
+
data["sequence_id"].to_i
|
48
|
+
}
|
49
|
+
}
|
33
50
|
|
34
|
-
Alephant.
|
51
|
+
thread = Alephant::Alephant.new(opts).run!
|
52
|
+
thread.join
|
53
|
+
```
|
54
|
+
|
55
|
+
Provide a view in a folder:
|
56
|
+
|
57
|
+
```
|
58
|
+
└── views
|
59
|
+
├── models
|
60
|
+
│ └── foo.rb
|
61
|
+
└── templates
|
62
|
+
└── foo.mustache
|
63
|
+
```
|
64
|
+
|
65
|
+
**SQS Message Format**
|
66
|
+
|
67
|
+
```json
|
68
|
+
{
|
69
|
+
"content": "hello world",
|
70
|
+
"sequential_id": 1
|
71
|
+
}
|
72
|
+
```
|
73
|
+
|
74
|
+
**foo.rb**
|
75
|
+
```rb
|
76
|
+
module MyApp
|
77
|
+
module Views
|
78
|
+
class Foo < Alephant::Views::Base
|
79
|
+
def content
|
80
|
+
@data['content']
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
**foo.mustache**
|
88
|
+
```mustache
|
89
|
+
{{content}}
|
90
|
+
```
|
91
|
+
|
92
|
+
**S3 Output**
|
93
|
+
```
|
94
|
+
hello world
|
35
95
|
```
|
36
96
|
|
data/lib/alephant.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
|
1
3
|
require 'aws-sdk'
|
2
|
-
require 'env'
|
3
4
|
require 'json'
|
4
5
|
|
6
|
+
require_relative 'env'
|
7
|
+
|
8
|
+
require 'alephant/models/queue'
|
5
9
|
require 'alephant/models/cache'
|
6
10
|
require 'alephant/models/renderer'
|
7
11
|
require 'alephant/models/sequencer'
|
@@ -9,31 +13,72 @@ require 'alephant/models/sequencer'
|
|
9
13
|
require 'alephant/errors'
|
10
14
|
require 'alephant/views'
|
11
15
|
|
12
|
-
|
13
16
|
module Alephant
|
17
|
+
class Alephant
|
18
|
+
attr_reader :sequencer, :queue, :cache, :renderer
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
VALID_OPTS = [
|
21
|
+
:s3_bucket_id,
|
22
|
+
:s3_object_path,
|
23
|
+
:s3_object_id,
|
24
|
+
:table_name,
|
25
|
+
:sqs_queue_id,
|
26
|
+
:view_id,
|
27
|
+
:sequential_proc,
|
28
|
+
:set_last_seen_proc
|
29
|
+
]
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
queue.poll do |msg|
|
24
|
-
data = JSON.parse(msg.body)
|
31
|
+
def initialize(opts = {})
|
32
|
+
set_opts(opts)
|
25
33
|
|
26
|
-
|
27
|
-
|
34
|
+
@sequencer = Sequencer.new(
|
35
|
+
{
|
36
|
+
:table_name => @table_name
|
37
|
+
},
|
38
|
+
@sqs_queue_id
|
39
|
+
)
|
40
|
+
|
41
|
+
@queue = Queue.new(@sqs_queue_id)
|
42
|
+
@cache = Cache.new(@s3_bucket_id, @s3_object_path)
|
43
|
+
@renderer = Renderer.new(@view_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse(msg)
|
47
|
+
JSON.parse(msg)
|
48
|
+
end
|
49
|
+
|
50
|
+
def write(data)
|
51
|
+
@cache.put(
|
52
|
+
@s3_object_id,
|
53
|
+
@renderer.render(data)
|
54
|
+
)
|
55
|
+
end
|
28
56
|
|
29
|
-
|
30
|
-
|
31
|
-
|
57
|
+
def receive(msg)
|
58
|
+
data = parse(msg.body)
|
59
|
+
|
60
|
+
if @sequencer.sequential?(data, &@sequential_proc)
|
61
|
+
write data
|
62
|
+
@sequencer.set_last_seen(data, &@set_last_seen_proc)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def run!
|
67
|
+
Thread.new do
|
68
|
+
@queue.poll { |msg| receive(msg) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def set_opts(opts)
|
74
|
+
VALID_OPTS.each do | k |
|
75
|
+
v = opts.has_key?(k) ? opts[k] : nil
|
76
|
+
singleton_class.class_eval do
|
77
|
+
attr_accessor k
|
32
78
|
end
|
79
|
+
send("#{k}=", v)
|
33
80
|
end
|
34
81
|
end
|
35
82
|
|
36
|
-
thread
|
37
83
|
end
|
38
84
|
end
|
39
|
-
|
@@ -2,19 +2,23 @@ require 'aws-sdk'
|
|
2
2
|
|
3
3
|
module Alephant
|
4
4
|
class Cache
|
5
|
+
attr_reader :id, :bucket, :path
|
5
6
|
|
6
|
-
def initialize(id)
|
7
|
-
@
|
8
|
-
|
7
|
+
def initialize(id, path)
|
8
|
+
@id = id
|
9
|
+
@path = path
|
9
10
|
|
10
|
-
|
11
|
-
@
|
11
|
+
s3 = AWS::S3.new
|
12
|
+
@bucket = s3.buckets[id]
|
12
13
|
end
|
13
14
|
|
14
|
-
def put(
|
15
|
-
@
|
15
|
+
def put(id, data)
|
16
|
+
@bucket.objects["#{@path}/#{id}"].write(data)
|
16
17
|
end
|
17
18
|
|
19
|
+
def get(id)
|
20
|
+
@bucket.objects["#{@path}/#{id}"].read
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Alephant
|
4
|
+
class Queue
|
5
|
+
attr_accessor :q
|
6
|
+
|
7
|
+
def initialize(id)
|
8
|
+
@sqs = AWS::SQS.new
|
9
|
+
@q = @sqs.queues[id]
|
10
|
+
|
11
|
+
unless @q.exists?
|
12
|
+
@q = @sqs.queues.create(id)
|
13
|
+
sleep_until_queue_exists
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def sleep_until_queue_exists
|
18
|
+
sleep 1 until @q.exists?
|
19
|
+
end
|
20
|
+
|
21
|
+
def poll(*args, &block)
|
22
|
+
@q.poll(*args, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,40 +2,64 @@ require 'aws-sdk'
|
|
2
2
|
|
3
3
|
module Alephant
|
4
4
|
class Sequencer
|
5
|
-
attr_reader :id, :
|
5
|
+
attr_reader :id, :table_name, :table_conf
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
def table_conf_defaults
|
8
|
+
{
|
9
|
+
:write_units => 5,
|
10
|
+
:read_units => 10,
|
11
|
+
:schema => {
|
12
|
+
:hash_key => {
|
13
|
+
:key => :string,
|
14
|
+
:value => :string
|
15
|
+
}
|
15
16
|
}
|
16
17
|
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(opts, id)
|
21
|
+
dynamo_db = AWS::DynamoDB.new
|
22
|
+
|
23
|
+
@id = id
|
24
|
+
@table_name = opts[:table_name]
|
25
|
+
@table_conf = opts[:table_conf] || table_conf_defaults
|
26
|
+
@table = dynamo_db.tables[@table_name]
|
17
27
|
|
18
|
-
@table = dynamo_db.tables[id]
|
19
28
|
begin
|
20
29
|
sleep_until_table_active
|
21
30
|
rescue AWS::DynamoDB::Errors::ResourceNotFoundException
|
22
|
-
|
23
|
-
|
31
|
+
@table = dynamo_db.tables.create(
|
32
|
+
@table_name,
|
33
|
+
@table_conf[:read_units],
|
34
|
+
@table_conf[:write_units],
|
35
|
+
@table_conf[:schema]
|
36
|
+
)
|
24
37
|
|
25
38
|
sleep_until_table_active
|
26
39
|
end
|
40
|
+
end
|
27
41
|
|
42
|
+
def sequential?(data)
|
43
|
+
if block_given?
|
44
|
+
yield(get_last_seen, data)
|
45
|
+
else
|
46
|
+
get_last_seen < data["sequence_id"].to_i
|
47
|
+
end
|
28
48
|
end
|
29
49
|
|
30
|
-
def
|
31
|
-
|
50
|
+
def set_last_seen(data)
|
51
|
+
last_seen_id = block_given? ? yield(data) : data["sequence_id"]
|
52
|
+
|
53
|
+
batch = AWS::DynamoDB::BatchWrite.new
|
54
|
+
batch.put(@table_name, [:key => @id,:value => last_seen_id])
|
55
|
+
batch.process!
|
32
56
|
end
|
33
57
|
|
34
|
-
def
|
58
|
+
def get_last_seen
|
35
59
|
begin
|
36
60
|
@table.batch_get(
|
37
61
|
['value'],
|
38
|
-
[
|
62
|
+
[@id],
|
39
63
|
{
|
40
64
|
:consistent_read => true
|
41
65
|
}
|
@@ -45,13 +69,6 @@ module Alephant
|
|
45
69
|
end
|
46
70
|
end
|
47
71
|
|
48
|
-
def last_seen=(last_seen)
|
49
|
-
batch = AWS::DynamoDB::BatchWrite.new
|
50
|
-
batch.put(@id, [:key => "last_seen",:value => last_seen])
|
51
|
-
batch.process!
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
72
|
def sleep_until_table_active
|
56
73
|
sleep 1 until @table.status == :active
|
57
74
|
end
|
data/lib/alephant/version.rb
CHANGED
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alephant::Alephant do
|
4
|
+
subject { Alephant::Alephant }
|
5
|
+
|
6
|
+
describe "initialize(opts = {})" do
|
7
|
+
before(:each) do
|
8
|
+
sequencer = double()
|
9
|
+
queue = double()
|
10
|
+
cache = double()
|
11
|
+
renderer = double()
|
12
|
+
|
13
|
+
Alephant::Sequencer.any_instance.stub(:initialize).and_return(sequencer)
|
14
|
+
Alephant::Queue.any_instance.stub(:initialize).and_return(queue)
|
15
|
+
Alephant::Cache.any_instance.stub(:initialize).and_return(cache)
|
16
|
+
Alephant::Renderer.any_instance.stub(:initialize).and_return(renderer)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets specified options" do
|
20
|
+
instance = subject.new({
|
21
|
+
:s3_bucket_id => :s3_bucket_id,
|
22
|
+
:s3_object_path => :s3_object_path,
|
23
|
+
:s3_object_id => :s3_object_id,
|
24
|
+
:table_name => :table_name,
|
25
|
+
:sqs_queue_id => :sqs_queue_id,
|
26
|
+
:view_id => :view_id,
|
27
|
+
:sequential_proc => :sequential_proc,
|
28
|
+
:set_last_seen_proc => :set_last_seen_proc
|
29
|
+
})
|
30
|
+
|
31
|
+
expect(instance.s3_bucket_id).to eq(:s3_bucket_id);
|
32
|
+
expect(instance.s3_object_path).to eq(:s3_object_path);
|
33
|
+
expect(instance.s3_object_id).to eq(:s3_object_id);
|
34
|
+
expect(instance.table_name).to eq(:table_name);
|
35
|
+
expect(instance.sqs_queue_id).to eq(:sqs_queue_id);
|
36
|
+
expect(instance.view_id).to eq(:view_id);
|
37
|
+
expect(instance.sequential_proc).to eq(:sequential_proc);
|
38
|
+
expect(instance.set_last_seen_proc).to eq(:set_last_seen_proc);
|
39
|
+
end
|
40
|
+
|
41
|
+
it "sets unspecified options to nil" do
|
42
|
+
instance = subject.new
|
43
|
+
|
44
|
+
expect(instance.s3_bucket_id).to eq(nil);
|
45
|
+
expect(instance.s3_object_path).to eq(nil);
|
46
|
+
expect(instance.s3_object_id).to eq(nil);
|
47
|
+
expect(instance.table_name).to eq(nil);
|
48
|
+
expect(instance.sqs_queue_id).to eq(nil);
|
49
|
+
expect(instance.view_id).to eq(nil);
|
50
|
+
expect(instance.sequential_proc).to eq(nil);
|
51
|
+
expect(instance.set_last_seen_proc).to eq(nil);
|
52
|
+
end
|
53
|
+
|
54
|
+
context "initializes @sequencer" do
|
55
|
+
it "with Sequencer.new({ :table_name => :table_name }, @sqs_queue_id)" do
|
56
|
+
Alephant::Sequencer.should_receive(:new)
|
57
|
+
.with({ :table_name => :table_name }, :sqs_queue_id)
|
58
|
+
|
59
|
+
instance = subject.new({
|
60
|
+
:table_name => :table_name,
|
61
|
+
:sqs_queue_id => :sqs_queue_id
|
62
|
+
})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "initializes @queue" do
|
67
|
+
it "with Queue.new(@sqs_queue_id)" do
|
68
|
+
Alephant::Queue.should_receive(:new).with(:sqs_queue_id)
|
69
|
+
|
70
|
+
instance = subject.new({
|
71
|
+
:sqs_queue_id => :sqs_queue_id
|
72
|
+
})
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "initializes @cache" do
|
77
|
+
it "with Cache.new(@s3_bucket_id, @s3_object_path)" do
|
78
|
+
Alephant::Cache.should_receive(:new).with(:s3_bucket_id, :s3_object_path)
|
79
|
+
|
80
|
+
instance = subject.new({
|
81
|
+
:s3_bucket_id => :s3_bucket_id,
|
82
|
+
:s3_object_path => :s3_object_path
|
83
|
+
})
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "initializes @renderer" do
|
88
|
+
it "with Renderer.new(@view_id)" do
|
89
|
+
Alephant::Renderer.should_receive(:new).with(:view_id)
|
90
|
+
|
91
|
+
instance = subject.new({
|
92
|
+
:view_id => :view_id
|
93
|
+
})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "run!" do
|
99
|
+
before(:each) do
|
100
|
+
sequencer = double()
|
101
|
+
queue = double()
|
102
|
+
cache = double()
|
103
|
+
renderer = double()
|
104
|
+
|
105
|
+
Alephant::Sequencer.any_instance.stub(:initialize).and_return(sequencer)
|
106
|
+
Alephant::Queue.any_instance.stub(:initialize).and_return(queue)
|
107
|
+
Alephant::Cache.any_instance.stub(:initialize).and_return(cache)
|
108
|
+
Alephant::Renderer.any_instance.stub(:initialize).and_return(renderer)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns a Thread" do
|
112
|
+
instance = subject.new({
|
113
|
+
:sqs_queue_id => :sqs_queue_id
|
114
|
+
})
|
115
|
+
|
116
|
+
expect(instance.run!).to be_a(Thread)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "calls @queue.poll" do
|
120
|
+
instance = subject.new({
|
121
|
+
:sqs_queue_id => :sqs_queue_id
|
122
|
+
})
|
123
|
+
|
124
|
+
instance.should_receive(:receive).with(:msg)
|
125
|
+
|
126
|
+
expect_any_instance_of(Alephant::Queue).to receive(:poll).and_yield(:msg)
|
127
|
+
|
128
|
+
t = instance.run!
|
129
|
+
t.join
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "receive(msg)" do
|
134
|
+
before(:each) do
|
135
|
+
sequencer = double()
|
136
|
+
queue = double()
|
137
|
+
cache = double()
|
138
|
+
renderer = double()
|
139
|
+
|
140
|
+
Alephant::Sequencer.any_instance.stub(:initialize).and_return(sequencer)
|
141
|
+
Alephant::Queue.any_instance.stub(:initialize).and_return(queue)
|
142
|
+
Alephant::Cache.any_instance.stub(:initialize).and_return(cache)
|
143
|
+
Alephant::Renderer.any_instance.stub(:initialize).and_return(renderer)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "takes json as an argument" do
|
147
|
+
instance = subject.new
|
148
|
+
msg = double()
|
149
|
+
msg.stub(:body).and_return('notjson')
|
150
|
+
|
151
|
+
expect { instance.receive(msg) }.to raise_error(JSON::ParserError);
|
152
|
+
end
|
153
|
+
|
154
|
+
it "writes data to cache if sequential order is true" do
|
155
|
+
data = "{ \"foo\":\"bar\" }"
|
156
|
+
msg = double()
|
157
|
+
msg.stub(:body).and_return(data)
|
158
|
+
|
159
|
+
instance = subject.new
|
160
|
+
|
161
|
+
Alephant::Sequencer.any_instance.stub(:sequential?).and_return(true)
|
162
|
+
Alephant::Sequencer.any_instance.stub(:set_last_seen)
|
163
|
+
|
164
|
+
instance.should_receive(:write).with(JSON.parse(data))
|
165
|
+
instance.receive(msg)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "write(data)" do
|
170
|
+
before(:each) do
|
171
|
+
sequencer = double()
|
172
|
+
queue = double()
|
173
|
+
cache = double()
|
174
|
+
renderer = double()
|
175
|
+
|
176
|
+
Alephant::Sequencer.any_instance.stub(:initialize).and_return(sequencer)
|
177
|
+
Alephant::Queue.any_instance.stub(:initialize).and_return(queue)
|
178
|
+
Alephant::Cache.any_instance.stub(:initialize).and_return(cache)
|
179
|
+
Alephant::Renderer.any_instance.stub(:initialize).and_return(renderer)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "puts rendered data into the S3 Cache" do
|
183
|
+
Alephant::Cache.any_instance.should_receive(:put).with(:s3_object_id, :content)
|
184
|
+
Alephant::Renderer.any_instance.stub(:render).and_return(:content)
|
185
|
+
|
186
|
+
instance = subject.new({
|
187
|
+
:s3_object_id => :s3_object_id
|
188
|
+
})
|
189
|
+
|
190
|
+
instance.write(:content)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/spec/cache_spec.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
describe Alephant::Cache do
|
5
|
+
let(:id) { :id }
|
6
|
+
let(:path) { :path }
|
7
|
+
let(:data) { :data }
|
8
|
+
subject { Alephant::Cache }
|
9
|
+
|
10
|
+
describe "initialize(id, path)" do
|
11
|
+
it "sets and exposes id, path instance variables " do
|
12
|
+
instance = subject.new(id, path)
|
13
|
+
expect(instance.id).to eq(id)
|
14
|
+
expect(instance.path).to eq(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sets bucket instance variable as S3 bucket with id" do
|
18
|
+
instance = subject.new(id, path)
|
19
|
+
|
20
|
+
expect(instance.bucket).to be_an AWS::S3::Bucket
|
21
|
+
expect(instance.bucket.name).to eq('id')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "put(id, data)" do
|
26
|
+
it "sets bucket path/id content data" do
|
27
|
+
s3_object_collection = double()
|
28
|
+
s3_object_collection.should_receive(:write).with(:data)
|
29
|
+
|
30
|
+
s3_bucket = double()
|
31
|
+
s3_bucket.should_receive(:objects).and_return(
|
32
|
+
{
|
33
|
+
"path/id" => s3_object_collection
|
34
|
+
}
|
35
|
+
)
|
36
|
+
|
37
|
+
AWS::S3.any_instance.stub(:buckets).and_return({ id => s3_bucket })
|
38
|
+
instance = subject.new(id, path)
|
39
|
+
|
40
|
+
instance.put(id, data);
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "get(id)" do
|
45
|
+
it "gets bucket path/id content data" do
|
46
|
+
s3_object_collection = double()
|
47
|
+
s3_object_collection.should_receive(:read)
|
48
|
+
|
49
|
+
s3_bucket = double()
|
50
|
+
s3_bucket.should_receive(:objects).and_return(
|
51
|
+
{
|
52
|
+
"path/id" => s3_object_collection
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
AWS::S3.any_instance.stub(:buckets).and_return({ id => s3_bucket })
|
57
|
+
|
58
|
+
instance = subject.new(id, path)
|
59
|
+
instance.get(id);
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/spec/queue_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alephant::Queue do
|
4
|
+
subject { Alephant::Queue }
|
5
|
+
|
6
|
+
describe "initialize(id)" do
|
7
|
+
it "sets @q to an instance of AWS:SQS::Queue" do
|
8
|
+
AWS::SQS::Queue.any_instance.stub(:exists?)
|
9
|
+
.and_return(true)
|
10
|
+
|
11
|
+
instance = subject.new(:id)
|
12
|
+
expect(instance.q).to be_a(AWS::SQS::Queue)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "@q.exists? == false" do
|
16
|
+
it "@q = AWS::SQS.new.queues.create(id), then sleep_until_queue_exists" do
|
17
|
+
queue = double()
|
18
|
+
queue.stub(:exists?).and_return(false)
|
19
|
+
|
20
|
+
queue_collection = double()
|
21
|
+
queue_collection.should_receive(:create).with(:id)
|
22
|
+
.and_return(queue)
|
23
|
+
|
24
|
+
sqs = double()
|
25
|
+
sqs.should_receive(:queues)
|
26
|
+
.and_return({ :id => queue }, queue_collection)
|
27
|
+
|
28
|
+
AWS::SQS.should_receive(:new).and_return(sqs)
|
29
|
+
|
30
|
+
subject.any_instance.should_receive(:sleep_until_queue_exists)
|
31
|
+
|
32
|
+
instance = subject.new(:id)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "poll(*args, &block)" do
|
38
|
+
it "calls @q.poll(*args, &block)" do
|
39
|
+
block = double()
|
40
|
+
block.should_receive(:called)
|
41
|
+
|
42
|
+
AWS::SQS::Queue.any_instance.stub(:exists?)
|
43
|
+
.and_return(true)
|
44
|
+
|
45
|
+
AWS::SQS::Queue.any_instance.should_receive(:poll).with(:arg).and_yield
|
46
|
+
|
47
|
+
subject.new(:id).poll(:arg) do
|
48
|
+
block.called
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "sleep_until_queue_exists" do
|
54
|
+
context "@q.exists? == true" do
|
55
|
+
it "should not call sleep" do
|
56
|
+
AWS::SQS::Queue.any_instance.stub(:exists?)
|
57
|
+
.and_return(true)
|
58
|
+
|
59
|
+
Alephant::Queue.any_instance.stub(:sleep)
|
60
|
+
Alephant::Queue.any_instance.should_not_receive(:sleep)
|
61
|
+
|
62
|
+
subject.new(:id).sleep_until_queue_exists
|
63
|
+
end
|
64
|
+
end
|
65
|
+
context "@q.exists? == false" do
|
66
|
+
it "should call sleep(1)" do
|
67
|
+
AWS::SQS::Queue.any_instance.stub(:exists?)
|
68
|
+
.and_return(true, false, true)
|
69
|
+
|
70
|
+
Alephant::Queue.any_instance.stub(:sleep)
|
71
|
+
Alephant::Queue.any_instance.should_receive(:sleep).with(1)
|
72
|
+
|
73
|
+
subject.new(:id).sleep_until_queue_exists
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alephant::Sequencer do
|
4
|
+
subject { Alephant::Sequencer }
|
5
|
+
|
6
|
+
describe "initialize(opts, id)" do
|
7
|
+
|
8
|
+
it "sets @id, @table_name and @table_conf" do
|
9
|
+
AWS::DynamoDB.any_instance.stub(:initialize).and_return({
|
10
|
+
:tables => {
|
11
|
+
:status => :active
|
12
|
+
}
|
13
|
+
})
|
14
|
+
|
15
|
+
Alephant::Sequencer.any_instance.stub(:sleep_until_table_active)
|
16
|
+
|
17
|
+
instance = subject.new({
|
18
|
+
:table_name => :table_name,
|
19
|
+
:table_conf => :table_conf
|
20
|
+
}, :sqs_queue_id)
|
21
|
+
|
22
|
+
expect(instance.id).to eq(:sqs_queue_id)
|
23
|
+
expect(instance.table_name).to eq(:table_name)
|
24
|
+
expect(instance.table_conf).to eq(:table_conf)
|
25
|
+
end
|
26
|
+
|
27
|
+
context "sleep_until_table_active raises" do
|
28
|
+
context "AWS::DynamoDB::Errors::ResourceNotFoundException" do
|
29
|
+
it "dynamo_db.tables.create(@table_name, opts) then sleep_until_table_active" do
|
30
|
+
opts = {
|
31
|
+
:table_name => :table_name,
|
32
|
+
:table_conf => {
|
33
|
+
:read_units => :read_units,
|
34
|
+
:write_units => :write_units,
|
35
|
+
:schema => :schema
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
table_collection = double()
|
40
|
+
table_collection.should_receive(:create).with(
|
41
|
+
opts[:table_name],
|
42
|
+
opts[:table_conf][:read_units],
|
43
|
+
opts[:table_conf][:write_units],
|
44
|
+
opts[:table_conf][:schema]
|
45
|
+
)
|
46
|
+
|
47
|
+
AWS::DynamoDB.any_instance.stub(:tables).and_return({},table_collection)
|
48
|
+
|
49
|
+
Alephant::Sequencer
|
50
|
+
.any_instance
|
51
|
+
.stub(:sleep_until_table_active).and_yield do
|
52
|
+
@times_called ||= 0
|
53
|
+
raise AWS::DynamoDB::Errors::ResourceNotFoundException if @times_called == 0
|
54
|
+
@times_called += 1
|
55
|
+
end
|
56
|
+
|
57
|
+
subject.new(opts, :id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "sequential?(data)" do
|
64
|
+
before(:each) do
|
65
|
+
Alephant::Sequencer
|
66
|
+
.any_instance.stub(:initialize)
|
67
|
+
.and_return(double())
|
68
|
+
end
|
69
|
+
|
70
|
+
context "block_given? == true" do
|
71
|
+
it "yields to block" do
|
72
|
+
Alephant::Sequencer
|
73
|
+
.any_instance
|
74
|
+
.stub(:get_last_seen)
|
75
|
+
.and_return(1)
|
76
|
+
|
77
|
+
instance = subject.new
|
78
|
+
|
79
|
+
in_sequence = instance.sequential?(:data) do |last_seen, data|
|
80
|
+
:foo
|
81
|
+
end
|
82
|
+
|
83
|
+
expect(in_sequence).to eq(:foo)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "block_given? == false" do
|
88
|
+
context "get_last_seen < data['sequence_id']" do
|
89
|
+
it "returns true" do
|
90
|
+
Alephant::Sequencer
|
91
|
+
.any_instance
|
92
|
+
.stub(:get_last_seen)
|
93
|
+
.and_return(0)
|
94
|
+
|
95
|
+
instance = subject.new
|
96
|
+
|
97
|
+
data = { "sequence_id" => "1" }
|
98
|
+
|
99
|
+
expect(instance.sequential? data).to be(true)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "get_last_seen >= data['sequence_id']" do
|
104
|
+
it "returns false" do
|
105
|
+
Alephant::Sequencer
|
106
|
+
.any_instance
|
107
|
+
.stub(:get_last_seen)
|
108
|
+
.and_return(1)
|
109
|
+
|
110
|
+
instance = subject.new
|
111
|
+
|
112
|
+
data = { "sequence_id" => "0" }
|
113
|
+
|
114
|
+
expect(instance.sequential? data).to be(false)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alephant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Robert Kenny
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- lib/alephant/errors/view_model_not_found.rb
|
144
144
|
- lib/alephant/errors/view_template_not_found.rb
|
145
145
|
- lib/alephant/models/cache.rb
|
146
|
+
- lib/alephant/models/queue.rb
|
146
147
|
- lib/alephant/models/renderer.rb
|
147
148
|
- lib/alephant/models/sequencer.rb
|
148
149
|
- lib/alephant/util/string.rb
|
@@ -150,9 +151,13 @@ files:
|
|
150
151
|
- lib/alephant/views.rb
|
151
152
|
- lib/alephant/views/base.rb
|
152
153
|
- lib/env.rb
|
154
|
+
- spec/alephant_spec.rb
|
155
|
+
- spec/cache_spec.rb
|
153
156
|
- spec/fixtures/views/models/example.rb
|
154
157
|
- spec/fixtures/views/templates/example.mustache
|
158
|
+
- spec/queue_spec.rb
|
155
159
|
- spec/renderer_spec.rb
|
160
|
+
- spec/sequencer_spec.rb
|
156
161
|
- spec/spec_helper.rb
|
157
162
|
homepage: http://rubygems.org/gems/alephant
|
158
163
|
licenses:
|
@@ -179,7 +184,11 @@ signing_key:
|
|
179
184
|
specification_version: 4
|
180
185
|
summary: Static Publishing in the Cloud
|
181
186
|
test_files:
|
187
|
+
- spec/alephant_spec.rb
|
188
|
+
- spec/cache_spec.rb
|
182
189
|
- spec/fixtures/views/models/example.rb
|
183
190
|
- spec/fixtures/views/templates/example.mustache
|
191
|
+
- spec/queue_spec.rb
|
184
192
|
- spec/renderer_spec.rb
|
193
|
+
- spec/sequencer_spec.rb
|
185
194
|
- spec/spec_helper.rb
|