alephant-publisher 0.0.1

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.
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