alephant-publisher 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76c21b712ec1b6c51c2997742420dfb0fbd7ccd1
4
+ data.tar.gz: f36fa3db507bb27ea2e17b1cb0616003e2cbfe4e
5
+ SHA512:
6
+ metadata.gz: c3e23d4acc640ab07ffba6aa94e07a6eabad20774004cb20a4a71f79a49f0ea5977d8a5de91e8cd3b3aae1c765313e50177e54744759fb726c0279e6efe987cb
7
+ data.tar.gz: cbd43452f89a987beffd8d1c90ba20aa84685bea9a73184563c1529953c20b3a1de9ab046ee58e4c3d8461a293414685ef53639e8591426e2c3337f37a998af8
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rspec
19
+ .ruby-version
20
+ /config/*.yaml
21
+ /config/*.yml
22
+ /components
23
+ *.swp
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "jruby"
4
+ notifications:
5
+ email:
6
+ recipients:
7
+ - kenoir@gmail.com
8
+ on_failure: change
9
+ on_success: never
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in alephant-publishing.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/.+\.rb$})
4
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Integralist
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # Alephant::Publisher
2
+
3
+ Static publishing to S3 based on SQS messages
4
+
5
+ [![Build Status](https://travis-ci.org/BBC-News/alephant-publisher.png?branch=master)](https://travis-ci.org/BBC-News/alephant-publisher)
6
+
7
+ [![Dependency Status](https://gemnasium.com/BBC-News/alephant-publisher.png)](https://gemnasium.com/BBC-News/alephant-publisher)
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/alephant-publisher.png)](http://badge.fury.io/rb/alephant-publisher)
10
+
11
+ ## Dependencies
12
+
13
+ - JRuby 1.7.8
14
+ - An AWS account (you'll need to create):
15
+ - An S3 bucket
16
+ - An SQS Queue (if no sequence id provided then `sequence_id` will be used)
17
+ - A Dynamo DB table (optional, will attempt to create if can't be found)
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ gem 'alephant-publisher'
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install alephant-publisher
32
+
33
+ ## Setup
34
+
35
+ Ensure you have a `config/aws.yml` in the format:
36
+
37
+ ```yaml
38
+ access_key_id: ACCESS_KEY_ID
39
+ secret_access_key: SECRET_ACCESS_KEY
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ **In your application:**
45
+
46
+ ```rb
47
+ require 'alephant'
48
+
49
+ sequential_proc = Proc.new do |last_seen_id, data|
50
+ last_seen_id < data['sequence_id'].to_i
51
+ end
52
+
53
+ set_last_seen_proc = Proc.new do |data|
54
+ data['sequence_id'].to_i
55
+ end
56
+
57
+ opts = {
58
+ :s3_bucket_id => 'bucket-id',
59
+ :s3_object_path => 'path/to/object',
60
+ :s3_object_id => 'object_id',
61
+ :table_name => 'your_dynamo_db_table',
62
+ :sqs_queue_id => 'https://your_amazon_sqs_queue_url',
63
+ :sequential_proc => sequential_proc,
64
+ :set_last_seen_proc => set_last_seen_proc
65
+ }
66
+
67
+ logger = Logger.new
68
+
69
+ thread = Alephant::Alephant.new(opts, logger).run!
70
+ thread.join
71
+ ```
72
+
73
+ logger is optional, and must confirm to the Ruby standard logger interface
74
+
75
+ Provide a view in a folder (fixtures are optional):
76
+
77
+ ```
78
+ └── views
79
+ ├── models
80
+ │   └── foo.rb
81
+ ├── fixtures
82
+ │   └── foo.json
83
+ └── templates
84
+ └── foo.mustache
85
+ ```
86
+
87
+ **SQS Message Format**
88
+
89
+ ```json
90
+ {
91
+ "content": "hello world",
92
+ "sequential_id": 1
93
+ }
94
+ ```
95
+
96
+ **foo.rb**
97
+
98
+ ```rb
99
+ module MyApp
100
+ module Views
101
+ class Foo < Alephant::Views::Base
102
+ def content
103
+ @data['content']
104
+ end
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ **foo.mustache**
111
+
112
+ ```mustache
113
+ {{content}}
114
+ ```
115
+
116
+ **S3 Output**
117
+
118
+ ```
119
+ hello world
120
+ ```
121
+
122
+ ## Build the gem locally
123
+
124
+ If you want to test a modified version of the gem within your application without publishing it then you can follow these steps...
125
+
126
+ - `gem uninstall alephant-publisher`
127
+ - `gem build alephant-publisher.gemspec` (this will report the file generated which you reference in the next command)
128
+ - `gem install ./alephant-publisher-0.0.9.1-java.gem`
129
+
130
+ Now you can test the gem from within your application as you've installed the gem from the local version rather than your published version
131
+
132
+ ## Preview Server
133
+
134
+ `alephant preview`
135
+
136
+ The included preview server allows you to see the html generated by your
137
+ templates, both standalone and in the context of a page.
138
+
139
+ **Standalone**
140
+
141
+ `/component/:id/?:fixture?`
142
+
143
+ ### Full page preview
144
+
145
+ When viewing the component in the context of a page, you'll need to retrieve a
146
+ mustache template to provide the page context.
147
+
148
+ When performing an update a regex is applied to replace the static hostnames in
149
+ the retrieved html.
150
+
151
+ **Environment Variables**
152
+
153
+ ```sh
154
+ STATIC_HOST_REGEX="static.(sandbox.dev|int|test|stage|live).yourapp(i)?.com\/"
155
+ PREVIEW_TEMPLATE_URL="http://yourapp.com/template"
156
+ ```
157
+
158
+ **Example Remote Template**
159
+
160
+ `id` is the component/folder name
161
+
162
+ `template` is the mustache template file name
163
+
164
+ `location_in_page` should be something like (for example) `page_head` (specified within a `preview.mustache` file that the consuming application needs to create).
165
+
166
+ - `http://localhost:4567/component/id/template`
167
+ - `http://localhost:4567/preview/id/template/location_in_page`
168
+
169
+ `alephant update`
170
+
171
+ **In page**
172
+
173
+ `/preview/:id/:region/?:fixture?`
174
+
175
+ ## Contributing
176
+
177
+ 1. Fork it ( http://github.com/BBC-News/alephant-publisher/fork )
178
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
179
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
180
+ 4. Push to the branch (`git push origin my-new-feature`)
181
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
2
+
3
+ require 'rspec/core/rake_task'
4
+ require 'bundler/gem_tasks'
5
+ require 'alephant/publisher'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'alephant/publisher/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "alephant-publisher"
8
+ spec.version = Alephant::Publisher::VERSION
9
+ spec.authors = ["Integralist"]
10
+ spec.email = ["mark.mcdx@gmail.com"]
11
+ spec.summary = "Static publishing to S3 based on SQS messages"
12
+ spec.description = "Static publishing to S3 based on SQS messages"
13
+ spec.homepage = "https://github.com/BBC-News/alephant-publisher"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "rspec-nc"
24
+ spec.add_development_dependency "guard"
25
+ spec.add_development_dependency "guard-rspec"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "pry-remote"
28
+ spec.add_development_dependency "pry-nav"
29
+
30
+ spec.add_runtime_dependency 'sinatra'
31
+ spec.add_runtime_dependency 'faraday'
32
+ spec.add_runtime_dependency 'trollop'
33
+ spec.add_runtime_dependency 'rake'
34
+ spec.add_runtime_dependency 'aws-sdk', '~> 1.0'
35
+ spec.add_runtime_dependency 'mustache', '>= 0.99.5'
36
+ spec.add_runtime_dependency 'jsonpath'
37
+ spec.add_runtime_dependency 'crimp'
38
+ spec.add_runtime_dependency 'alephant-support'
39
+ spec.add_runtime_dependency 'alephant-sequencer'
40
+ spec.add_runtime_dependency 'alephant-cache'
41
+ spec.add_runtime_dependency 'alephant-logger'
42
+ spec.add_runtime_dependency 'alephant-renderer'
43
+ spec.add_runtime_dependency 'alephant-lookup'
44
+ spec.add_runtime_dependency 'alephant-preview'
45
+ end
@@ -0,0 +1,9 @@
1
+ require 'aws-sdk'
2
+ require 'yaml'
3
+
4
+ config_file = 'config/aws.yaml'
5
+
6
+ if File.exists? config_file
7
+ config = YAML.load(File.read(config_file))
8
+ AWS.config(config)
9
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'env'
2
+
3
+ require 'alephant/support'
4
+ require 'alephant/sequencer'
5
+ require 'alephant/cache'
6
+ require 'alephant/logger'
7
+ require 'alephant/views'
8
+ require 'alephant/renderer'
9
+ require 'alephant/lookup'
10
+
11
+ require "alephant/publisher/version"
12
+ require 'alephant/publisher/models/render_mapper'
13
+ require 'alephant/publisher/models/writer'
14
+ require 'alephant/publisher/models/queue'
15
+
16
+ module Alephant
17
+ module Publisher
18
+ include ::Alephant::Logger
19
+
20
+ def self.create(opts = {}, logger = nil)
21
+ Publisher.new(opts, logger)
22
+ end
23
+
24
+ private
25
+
26
+ class Publisher
27
+ attr_reader :sequencer, :queue, :writer, :parser
28
+
29
+ def initialize(opts, logger)
30
+ ::Alephant::Logger.set_logger(logger) unless logger.nil?
31
+
32
+ @parser = Support::Parser.new(
33
+ opts[:msg_vary_id_path]
34
+ )
35
+
36
+ @sequencer = Sequencer.create(
37
+ opts[:sequencer_table_name],
38
+ opts[:sqs_queue_url],
39
+ opts[:sequence_id_path]
40
+ )
41
+
42
+ @queue = Queue.new(
43
+ opts[:sqs_queue_url]
44
+ )
45
+
46
+ @writer = Writer.new(
47
+ opts.select do |k,v|
48
+ [
49
+ :renderer_id,
50
+ :s3_bucket_id,
51
+ :s3_object_path,
52
+ :view_path,
53
+ :lookup_table_name
54
+ ].include? k
55
+ end
56
+ )
57
+ end
58
+
59
+ def run!
60
+ Thread.new do
61
+ @queue.poll { |msg| receive(msg) }
62
+ end
63
+ end
64
+
65
+ def receive(msg)
66
+ write msg if sequencer.sequential?(msg)
67
+ end
68
+
69
+ private
70
+
71
+ def write(msg)
72
+ writer.write(parser.parse(msg), sequencer.sequence_id_from(msg))
73
+ sequencer.set_last_seen(msg)
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,34 @@
1
+ require 'aws-sdk'
2
+ require 'alephant/logger'
3
+
4
+ module Alephant
5
+ module Publisher
6
+ class Queue
7
+ include ::Alephant::Logger
8
+
9
+ attr_accessor :q
10
+
11
+ def initialize(id)
12
+ @sqs = AWS::SQS.new
13
+ @q = @sqs.queues[id]
14
+
15
+ unless @q.exists?
16
+ @q = @sqs.queues.create(id)
17
+ sleep_until_queue_exists
18
+ logger.info("Queue.initialize: created queue with id #{id}")
19
+ end
20
+
21
+ logger.info("Queue.initialize: ended with id #{id}")
22
+ end
23
+
24
+ def sleep_until_queue_exists
25
+ sleep 1 until @q.exists?
26
+ end
27
+
28
+ def poll(*args, &block)
29
+ logger.info("Queue.poll: polling with arguments #{args}")
30
+ @q.poll(*args, &block)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ require 'alephant/logger'
2
+
3
+ module Alephant
4
+ module Publisher
5
+ class RenderMapper
6
+ include ::Alephant::Logger
7
+ DEFAULT_LOCATION = 'components'
8
+
9
+ def initialize(component_id, view_base_path=nil)
10
+ self.base_path = "#{view_base_path}/#{component_id}" unless view_base_path.nil?
11
+ @component_id = component_id
12
+ end
13
+
14
+ def base_path
15
+ @base_path || DEFAULT_LOCATION
16
+ end
17
+
18
+ def base_path=(path)
19
+ @base_path = File.directory?(path) ? path : (raise "Invalid path")
20
+ end
21
+
22
+ def generate(data)
23
+ template_locations.reduce({}) do |obj, file|
24
+ template_id = template_id_for file
25
+ obj.tap { |o| o[template_id] = create_renderer(template_id, data) }
26
+ end
27
+ end
28
+
29
+ def create_renderer(template_file, data)
30
+ model = instantiate_model(template_file, data)
31
+ Renderer.create(template_file, base_path, model)
32
+ end
33
+
34
+ private
35
+
36
+ def instantiate_model(view_id, data)
37
+ require model_location_for view_id
38
+ Views.get_registered_class(view_id).new(data)
39
+ end
40
+
41
+ def model_location_for(template_file)
42
+ File.join(base_path, 'models', "#{template_file}.rb")
43
+ end
44
+
45
+ def template_locations
46
+ Dir[template_base_path]
47
+ end
48
+
49
+ def template_base_path
50
+ "#{base_path}/templates/*"
51
+ end
52
+
53
+ def template_id_for(template_location)
54
+ template_location.split('/').last.sub(/\.mustache/, '')
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ require 'crimp'
2
+
3
+ module Alephant
4
+ module Publisher
5
+ class Writer
6
+ attr_reader :mapper, :cache
7
+
8
+ def initialize(opts)
9
+ @renderer_id =
10
+ opts[:renderer_id]
11
+ @cache = Cache.new(
12
+ opts[:s3_bucket_id],
13
+ opts[:s3_object_path]
14
+ )
15
+ @mapper = RenderMapper.new(
16
+ opts[:renderer_id],
17
+ opts[:view_path]
18
+ )
19
+ @lookup_table_name =
20
+ opts[:lookup_table_name]
21
+ end
22
+
23
+ def write(data, version = nil)
24
+ mapper.generate(data).each do |id, r|
25
+ store(id, r.render, data[:options], version)
26
+ end
27
+ end
28
+
29
+ private
30
+ def store(id, content, options, version)
31
+ location = location_for(
32
+ id,
33
+ Crimp.signature(options),
34
+ version
35
+ )
36
+
37
+ cache.put(location, content)
38
+ lookup(id).write(options, location)
39
+ end
40
+
41
+ def lookup(component_id)
42
+ Lookup.create(@lookup_table_name, component_id)
43
+ end
44
+
45
+ def location_for(component_id, options_hash, version = nil)
46
+ base_name = "#{@renderer_id}/#{component_id}/#{options_hash}"
47
+ version ? "#{base_name}/#{version}" : base_name
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ module Alephant
2
+ module Publisher
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module MyApp
2
+ class Bar < ::Alephant::Views::Base
3
+ def content
4
+ @data[:content]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module MyApp
2
+ class Foo < ::Alephant::Views::Base
3
+ def content
4
+ @data[:content]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ {{content}}
@@ -0,0 +1 @@
1
+ {{content}}
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe Alephant::Publisher do
4
+ let(:instance) { Alephant::Publisher.create }
5
+
6
+ before(:each) do
7
+ Alephant::Publisher::Writer.any_instance.stub(:initialize)
8
+ Alephant::Publisher::Queue.any_instance.stub(:initialize)
9
+ Alephant::Support::Parser.any_instance.stub(:initialize)
10
+ Alephant::Sequencer::Sequencer.any_instance.stub(:initialize)
11
+ end
12
+
13
+ describe "#initialize(opts = {}, logger)" do
14
+ it "sets parser, sequencer, queue and writer" do
15
+ expect(instance.writer).to be_a Alephant::Publisher::Writer
16
+ expect(instance.queue).to be_a Alephant::Publisher::Queue
17
+ expect(instance.parser).to be_a Alephant::Support::Parser
18
+ expect(instance.sequencer).to be_a Alephant::Sequencer::Sequencer
19
+ end
20
+ end
21
+
22
+ describe "#run!" do
23
+ it "returns a Thread" do
24
+ expect(instance.run!).to be_a(Thread)
25
+ end
26
+
27
+ it "calls @queue.poll" do
28
+ instance.should_receive(:receive).with(:msg)
29
+
30
+ expect_any_instance_of(Alephant::Publisher::Queue)
31
+ .to receive(:poll)
32
+ .and_yield(:msg)
33
+
34
+ t = instance.run!
35
+ t.join
36
+ end
37
+ end
38
+
39
+ describe "#receive(msg)" do
40
+ before(:each) do
41
+ Alephant::Support::Parser
42
+ .any_instance
43
+ .stub(:parse)
44
+ .and_return(:parsed_msg)
45
+
46
+ Alephant::Sequencer::Sequencer
47
+ .any_instance
48
+ .stub(:sequence_id_from)
49
+ .and_return(:sequence_id)
50
+
51
+ Alephant::Sequencer::Sequencer
52
+ .any_instance
53
+ .stub(:set_last_seen)
54
+ end
55
+
56
+ context "message is nonsequential" do
57
+ before(:each) do
58
+ Alephant::Sequencer::Sequencer
59
+ .any_instance
60
+ .stub(:sequential?)
61
+ .and_return(false)
62
+ end
63
+
64
+ it "should not call write" do
65
+ Alephant::Publisher::Writer
66
+ .any_instance
67
+ .should_not_receive(:write)
68
+
69
+ instance.receive(:msg)
70
+ end
71
+ end
72
+
73
+ context "message is sequential" do
74
+ before(:each) do
75
+ Alephant::Sequencer::Sequencer
76
+ .any_instance
77
+ .stub(:sequential?)
78
+ .and_return(true)
79
+ end
80
+
81
+ it "calls writer with a parsed message and sequence_id" do
82
+ Alephant::Publisher::Writer
83
+ .any_instance
84
+ .should_receive(:write)
85
+ .with(:parsed_msg, :sequence_id)
86
+
87
+ instance.receive(:msg)
88
+ end
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe Alephant::Publisher::Queue do
4
+ subject { Alephant::Publisher::Queue }
5
+
6
+ describe "initialize(id)" do
7
+ it "sets @q to an instance of AWS:SQS::Queue" do
8
+ AWS::SQS::Queue
9
+ .any_instance
10
+ .stub(:exists?)
11
+ .and_return(true)
12
+
13
+ instance = subject.new(:id)
14
+ expect(instance.q).to be_a(AWS::SQS::Queue)
15
+ end
16
+
17
+ context "@q.exists? == false" do
18
+ it "@q = AWS::SQS.new.queues.create(id), then sleep_until_queue_exists" do
19
+ queue = double()
20
+ queue.stub(:exists?).and_return(false)
21
+
22
+ queue_collection = double()
23
+ queue_collection
24
+ .should_receive(:create)
25
+ .with(:id)
26
+ .and_return(queue)
27
+
28
+ sqs = double()
29
+ sqs.should_receive(:queues)
30
+ .and_return({ :id => queue }, queue_collection)
31
+
32
+ AWS::SQS
33
+ .should_receive(:new)
34
+ .and_return(sqs)
35
+
36
+ subject
37
+ .any_instance
38
+ .should_receive(:sleep_until_queue_exists)
39
+
40
+ instance = subject.new(:id)
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "poll(*args, &block)" do
46
+ it "calls @q.poll(*args, &block)" do
47
+ block = double()
48
+ block.should_receive(:called)
49
+
50
+ AWS::SQS::Queue
51
+ .any_instance
52
+ .stub(:exists?)
53
+ .and_return(true)
54
+
55
+ AWS::SQS::Queue
56
+ .any_instance
57
+ .should_receive(:poll)
58
+ .with(:arg)
59
+ .and_yield
60
+
61
+ subject.new(:id).poll(:arg) do
62
+ block.called
63
+ end
64
+ end
65
+ end
66
+
67
+ describe "sleep_until_queue_exists" do
68
+ context "@q.exists? == true" do
69
+ it "should not call sleep" do
70
+ AWS::SQS::Queue
71
+ .any_instance
72
+ .stub(:exists?)
73
+ .and_return(true)
74
+
75
+ Alephant::Publisher::Queue
76
+ .any_instance
77
+ .stub(:sleep)
78
+
79
+ Alephant::Publisher::Queue
80
+ .any_instance
81
+ .should_not_receive(:sleep)
82
+
83
+ subject.new(:id).sleep_until_queue_exists
84
+ end
85
+ end
86
+ context "@q.exists? == false" do
87
+ it "should call sleep(1)" do
88
+ AWS::SQS::Queue
89
+ .any_instance
90
+ .stub(:exists?)
91
+ .and_return(true, false, true)
92
+
93
+ Alephant::Publisher::Queue
94
+ .any_instance
95
+ .stub(:sleep)
96
+
97
+ Alephant::Publisher::Queue
98
+ .any_instance
99
+ .should_receive(:sleep)
100
+ .with(1)
101
+
102
+ subject.new(:id).sleep_until_queue_exists
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Alephant::Publisher::RenderMapper do
4
+ let(:component_id) { :foo }
5
+ let(:data) {{ :foo => :bar }}
6
+ let(:path) { File.join(File.dirname(__FILE__), 'fixtures/components') }
7
+
8
+ subject { Alephant::Publisher::RenderMapper }
9
+
10
+ before(:each) do
11
+ File.stub(:directory?).and_return(true)
12
+ end
13
+
14
+ describe "initialize(view_base_path)" do
15
+ context "view_base_path = invalid_path" do
16
+ it "should raise InvalidViewPath" do
17
+ File.stub(:directory?).and_return(false)
18
+ expect {
19
+ subject.new(component_id, './invalid_path')
20
+ }.to raise_error(
21
+ 'Invalid path'
22
+ )
23
+ end
24
+ end
25
+
26
+ context "view_base_path = '.'" do
27
+ it "sets base_path" do
28
+ expect(
29
+ subject.new(component_id, '.').base_path
30
+ ).to eq("./#{component_id}")
31
+ end
32
+ end
33
+
34
+ context "view_base_path = nil" do
35
+ it "sets base_path" do
36
+ expect(
37
+ subject.new(component_id).base_path
38
+ ).to eq(Alephant::Publisher::RenderMapper::DEFAULT_LOCATION)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "create_renderer(template_file, data)" do
44
+ it "Returns a valid renderer" do
45
+ expect(
46
+ subject.new(component_id, path)
47
+ .create_renderer('foo', { :content => 'hello'})
48
+ ).to be_a(Alephant::Renderer::Mustache)
49
+ end
50
+
51
+ it "Returns expected rendered content from a render" do
52
+ expect(
53
+ subject.new(component_id, path)
54
+ .create_renderer('foo', { :content => 'hello'}).render
55
+ ).to eq("hello\n")
56
+ end
57
+ end
58
+
59
+ describe "generate(data)" do
60
+ it "calls create_renderer for each template found" do
61
+ expect(
62
+ subject.new(component_id, path).generate(data).size
63
+ ).to eq(2)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ # $: << File.join(File.dirname(__FILE__),"..", "lib")
2
+
3
+ require 'pry'
4
+ require 'alephant/publisher'
5
+ require 'alephant/publisher/models/writer'
6
+ require 'alephant/publisher/models/queue'
7
+ require 'alephant/publisher/models/render_mapper'
8
+ # require 'logger'
9
+ require 'alephant/renderer'
10
+ require 'alephant/support/parser'
11
+
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Alephant::Publisher::Writer do
4
+ before(:each) do
5
+ Alephant::Publisher::RenderMapper
6
+ .any_instance
7
+ .stub(:initialize)
8
+
9
+ Alephant::Cache
10
+ .any_instance
11
+ .stub(:initialize)
12
+ end
13
+
14
+ subject do
15
+ Alephant::Publisher::Writer.new({
16
+ :renderer_id => 'renderer_id',
17
+ :lookup_table_name => 'lookup_table_name'
18
+ })
19
+ end
20
+
21
+ describe "#write(data, version)" do
22
+ before(:each) do
23
+ Alephant::Publisher::RenderMapper
24
+ .any_instance
25
+ .stub(:generate)
26
+ .and_return({
27
+ 'component_id' => Struct.new(:render).new('content')
28
+ })
29
+
30
+ end
31
+
32
+ it "should write the correct lookup location" do
33
+ options = { :key => :value }
34
+ data = { :options => options }
35
+
36
+ Alephant::Cache.any_instance
37
+ .stub(:put)
38
+
39
+ Alephant::Lookup
40
+ .should_receive(:create)
41
+ .with('lookup_table_name', 'component_id')
42
+ .and_call_original
43
+
44
+ Alephant::Lookup::Lookup.any_instance
45
+ .stub(:initialize)
46
+
47
+ Alephant::Lookup::Lookup.any_instance
48
+ .should_receive(:write)
49
+ .with(options, 'renderer_id/component_id/42de5e5c6f74b9fe4d956704a6d9e1c7/0')
50
+
51
+ subject.write(data, 0)
52
+ end
53
+
54
+ it "should put the correct location, content to cache" do
55
+ Alephant::Lookup::Lookup
56
+ .any_instance
57
+ .stub(:initialize)
58
+
59
+ Alephant::Lookup::Lookup
60
+ .any_instance
61
+ .stub(:write)
62
+
63
+ Alephant::Cache.any_instance
64
+ .should_receive(:put)
65
+ .with('renderer_id/component_id/35589a1cc0b3ca90fc52d0e711c0c434/0', 'content')
66
+
67
+ subject.write({}, 0)
68
+ end
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,398 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alephant-publisher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Integralist
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: '1.5'
25
+ prerelease: false
26
+ type: :development
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ prerelease: false
40
+ type: :development
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-nc
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ prerelease: false
54
+ type: :development
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ prerelease: false
68
+ type: :development
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ prerelease: false
82
+ type: :development
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ prerelease: false
96
+ type: :development
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-remote
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ prerelease: false
110
+ type: :development
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-nav
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ prerelease: false
124
+ type: :development
125
+ - !ruby/object:Gem::Dependency
126
+ name: sinatra
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ prerelease: false
138
+ type: :runtime
139
+ - !ruby/object:Gem::Dependency
140
+ name: faraday
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ prerelease: false
152
+ type: :runtime
153
+ - !ruby/object:Gem::Dependency
154
+ name: trollop
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ prerelease: false
166
+ type: :runtime
167
+ - !ruby/object:Gem::Dependency
168
+ name: rake
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - '>='
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ prerelease: false
180
+ type: :runtime
181
+ - !ruby/object:Gem::Dependency
182
+ name: aws-sdk
183
+ version_requirements: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ~>
186
+ - !ruby/object:Gem::Version
187
+ version: '1.0'
188
+ requirement: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ~>
191
+ - !ruby/object:Gem::Version
192
+ version: '1.0'
193
+ prerelease: false
194
+ type: :runtime
195
+ - !ruby/object:Gem::Dependency
196
+ name: mustache
197
+ version_requirements: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - '>='
200
+ - !ruby/object:Gem::Version
201
+ version: 0.99.5
202
+ requirement: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - '>='
205
+ - !ruby/object:Gem::Version
206
+ version: 0.99.5
207
+ prerelease: false
208
+ type: :runtime
209
+ - !ruby/object:Gem::Dependency
210
+ name: jsonpath
211
+ version_requirements: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - '>='
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ requirement: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - '>='
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ prerelease: false
222
+ type: :runtime
223
+ - !ruby/object:Gem::Dependency
224
+ name: crimp
225
+ version_requirements: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - '>='
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ requirement: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - '>='
233
+ - !ruby/object:Gem::Version
234
+ version: '0'
235
+ prerelease: false
236
+ type: :runtime
237
+ - !ruby/object:Gem::Dependency
238
+ name: alephant-support
239
+ version_requirements: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - '>='
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ requirement: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - '>='
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ prerelease: false
250
+ type: :runtime
251
+ - !ruby/object:Gem::Dependency
252
+ name: alephant-sequencer
253
+ version_requirements: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - '>='
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ requirement: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - '>='
261
+ - !ruby/object:Gem::Version
262
+ version: '0'
263
+ prerelease: false
264
+ type: :runtime
265
+ - !ruby/object:Gem::Dependency
266
+ name: alephant-cache
267
+ version_requirements: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - '>='
270
+ - !ruby/object:Gem::Version
271
+ version: '0'
272
+ requirement: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - '>='
275
+ - !ruby/object:Gem::Version
276
+ version: '0'
277
+ prerelease: false
278
+ type: :runtime
279
+ - !ruby/object:Gem::Dependency
280
+ name: alephant-logger
281
+ version_requirements: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - '>='
284
+ - !ruby/object:Gem::Version
285
+ version: '0'
286
+ requirement: !ruby/object:Gem::Requirement
287
+ requirements:
288
+ - - '>='
289
+ - !ruby/object:Gem::Version
290
+ version: '0'
291
+ prerelease: false
292
+ type: :runtime
293
+ - !ruby/object:Gem::Dependency
294
+ name: alephant-renderer
295
+ version_requirements: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - '>='
298
+ - !ruby/object:Gem::Version
299
+ version: '0'
300
+ requirement: !ruby/object:Gem::Requirement
301
+ requirements:
302
+ - - '>='
303
+ - !ruby/object:Gem::Version
304
+ version: '0'
305
+ prerelease: false
306
+ type: :runtime
307
+ - !ruby/object:Gem::Dependency
308
+ name: alephant-lookup
309
+ version_requirements: !ruby/object:Gem::Requirement
310
+ requirements:
311
+ - - '>='
312
+ - !ruby/object:Gem::Version
313
+ version: '0'
314
+ requirement: !ruby/object:Gem::Requirement
315
+ requirements:
316
+ - - '>='
317
+ - !ruby/object:Gem::Version
318
+ version: '0'
319
+ prerelease: false
320
+ type: :runtime
321
+ - !ruby/object:Gem::Dependency
322
+ name: alephant-preview
323
+ version_requirements: !ruby/object:Gem::Requirement
324
+ requirements:
325
+ - - '>='
326
+ - !ruby/object:Gem::Version
327
+ version: '0'
328
+ requirement: !ruby/object:Gem::Requirement
329
+ requirements:
330
+ - - '>='
331
+ - !ruby/object:Gem::Version
332
+ version: '0'
333
+ prerelease: false
334
+ type: :runtime
335
+ description: Static publishing to S3 based on SQS messages
336
+ email:
337
+ - mark.mcdx@gmail.com
338
+ executables: []
339
+ extensions: []
340
+ extra_rdoc_files: []
341
+ files:
342
+ - .gitignore
343
+ - .travis.yml
344
+ - Gemfile
345
+ - Guardfile
346
+ - LICENSE.txt
347
+ - README.md
348
+ - Rakefile
349
+ - alephant-publisher.gemspec
350
+ - lib/alephant/env.rb
351
+ - lib/alephant/publisher.rb
352
+ - lib/alephant/publisher/models/queue.rb
353
+ - lib/alephant/publisher/models/render_mapper.rb
354
+ - lib/alephant/publisher/models/writer.rb
355
+ - lib/alephant/publisher/version.rb
356
+ - spec/fixtures/components/foo/models/bar.rb
357
+ - spec/fixtures/components/foo/models/foo.rb
358
+ - spec/fixtures/components/foo/templates/bar.mustache
359
+ - spec/fixtures/components/foo/templates/foo.mustache
360
+ - spec/publisher_spec.rb
361
+ - spec/queue_spec.rb
362
+ - spec/render_mapper_spec.rb
363
+ - spec/spec_helper.rb
364
+ - spec/writer_spec.rb
365
+ homepage: https://github.com/BBC-News/alephant-publisher
366
+ licenses:
367
+ - MIT
368
+ metadata: {}
369
+ post_install_message:
370
+ rdoc_options: []
371
+ require_paths:
372
+ - lib
373
+ required_ruby_version: !ruby/object:Gem::Requirement
374
+ requirements:
375
+ - - '>='
376
+ - !ruby/object:Gem::Version
377
+ version: '0'
378
+ required_rubygems_version: !ruby/object:Gem::Requirement
379
+ requirements:
380
+ - - '>='
381
+ - !ruby/object:Gem::Version
382
+ version: '0'
383
+ requirements: []
384
+ rubyforge_project:
385
+ rubygems_version: 2.1.9
386
+ signing_key:
387
+ specification_version: 4
388
+ summary: Static publishing to S3 based on SQS messages
389
+ test_files:
390
+ - spec/fixtures/components/foo/models/bar.rb
391
+ - spec/fixtures/components/foo/models/foo.rb
392
+ - spec/fixtures/components/foo/templates/bar.mustache
393
+ - spec/fixtures/components/foo/templates/foo.mustache
394
+ - spec/publisher_spec.rb
395
+ - spec/queue_spec.rb
396
+ - spec/render_mapper_spec.rb
397
+ - spec/spec_helper.rb
398
+ - spec/writer_spec.rb