opener-s3-outlet 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +57 -0
- data/bin/s3-outlet-daemon +9 -0
- data/bin/s3-outlet-server +10 -0
- data/config.ru +4 -0
- data/config/aws.rb +8 -0
- data/exec/s3-outlet.rb +33 -0
- data/lib/opener/s3_outlet.rb +39 -0
- data/lib/opener/s3_outlet/public/outlet.css +58 -0
- data/lib/opener/s3_outlet/s3_output.rb +59 -0
- data/lib/opener/s3_outlet/server.rb +76 -0
- data/lib/opener/s3_outlet/version.rb +5 -0
- data/lib/opener/s3_outlet/views/index.erb +33 -0
- data/lib/opener/s3_outlet/views/show.erb +10 -0
- data/lib/opener/s3_outlet/views/show.html.erb +11 -0
- data/lib/opener/s3_outlet/visualizer.rb +278 -0
- data/opener-s3-outlet.gemspec +38 -0
- data/visualizer.rb +278 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 10341e220c8473c0a1122c20b7ab9dc7b1f8452e
|
4
|
+
data.tar.gz: e4a9dcf12a019669c8879838ff5f94ac3fc09a9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 975df73d82655fe9b781aed0e485a7805c68f3746442dd226d55217abe9acb411b98ea153174035dd242adf346f436ef49cc9b7b2f9a0a5325cebd727eecd073
|
7
|
+
data.tar.gz: ab71c20499c3bae2a3ca8a6e35710520786275c59d47b3ef49b2bacecfedbf133e9243206f258a14c1e136a0bbcb99befe467f026af76e335c3782fa0cf39360
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# S3-Outlet: Stores the results in an AWS S3 bucket.
|
2
|
+
|
3
|
+
Component that stores results from the Opener Web Services chain into an S3
|
4
|
+
bucket.
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
### As part of a Gemfile in a Ruby application
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'opener-s3-outlet',
|
14
|
+
:git=>"git@github.com:opener-project/s3-outlet.git"
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle install
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
The Opener S3 Outlet comes equipped with a simple webservice. To start the
|
23
|
+
webservice type:
|
24
|
+
|
25
|
+
s3-outlet-server
|
26
|
+
|
27
|
+
This will launch a mini webserver with the webservice. It defaults to port 9292,
|
28
|
+
so you can access it at:
|
29
|
+
|
30
|
+
http://localhost:9292
|
31
|
+
|
32
|
+
To launch it on a different port provide the ```-p [port-number]``` option like
|
33
|
+
this:
|
34
|
+
|
35
|
+
s3-outlet-server -p 1234
|
36
|
+
|
37
|
+
It then launches at ```http://localhost:1234```
|
38
|
+
|
39
|
+
When you run a chain of web services using callbacks, the last URL should be the
|
40
|
+
one that points to the S3 Outlet Web Service. A unique id is generated and once the
|
41
|
+
chain has finished processing the text, you can view the result in the URL that
|
42
|
+
you get.
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
### Procedure
|
47
|
+
|
48
|
+
1. Pull it
|
49
|
+
2. Create your feature branch (`git checkout -b features/my-new-feature`)
|
50
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
51
|
+
4. Push to the branch (`git push origin features/my-new-feature`)
|
52
|
+
5. If you're confident, merge your changes into master.
|
53
|
+
|
54
|
+
# What's next?
|
55
|
+
|
56
|
+
If you're interested in the opener-s3-outlet, you also might want to check
|
57
|
+
out opener-project/s3-outlet.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
# Without calling `Rack::Server#options` manually the CLI arguments will never
|
6
|
+
# be passed, thus the application can't be specified as a constructor argument.
|
7
|
+
server = Rack::Server.new
|
8
|
+
server.options[:config] = File.expand_path('../../config.ru', __FILE__)
|
9
|
+
|
10
|
+
server.start
|
data/config.ru
ADDED
data/config/aws.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# These details are tied to the "opener" AWS user.
|
2
|
+
AWS.config(
|
3
|
+
:access_key_id => 'AKIAIZH4THHTIHYS22NA',
|
4
|
+
:secret_access_key => 'BOPSE0KKP19009LgGpOmc43nrh9vs9DKMMBEyix/',
|
5
|
+
:sqs_region => 'eu-west-1',
|
6
|
+
:sqs_endpoint => 'sqs.eu-west-1.amazonaws.com',
|
7
|
+
:s3_endpoint => 's3-eu-west-1.amazonaws.com',
|
8
|
+
)
|
data/exec/s3-outlet.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'opener/daemons'
|
4
|
+
require_relative '../lib/opener/s3_outlet'
|
5
|
+
|
6
|
+
parser = Opener::Daemons::OptParser.new do |opts, options|
|
7
|
+
opts.on("-b", "--bucket NAME", "Bucket name") do |v|
|
8
|
+
options[:bucket] = v
|
9
|
+
end
|
10
|
+
opts.on("-d", "--directory NAME", "Directory name") do |v|
|
11
|
+
options[:directory] = v
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class S3OutletFactory
|
16
|
+
attr_reader :dir, :bucket
|
17
|
+
|
18
|
+
def initialize(options)
|
19
|
+
@bucket = options.fetch(:bucket)
|
20
|
+
@dir = options.fetch(:directory)
|
21
|
+
end
|
22
|
+
|
23
|
+
def new
|
24
|
+
Opener::S3Outlet.new(:directory=>dir, :bucket=>bucket)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
options = parser.parse!(ARGV)
|
29
|
+
factory = S3OutletFactory.new(options)
|
30
|
+
|
31
|
+
daemon = Opener::Daemons::Daemon.new(factory, options)
|
32
|
+
daemon.start
|
33
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
require_relative '../../config/aws'
|
4
|
+
require_relative 's3_outlet/s3_output'
|
5
|
+
require_relative 's3_outlet/version'
|
6
|
+
require_relative 's3_outlet/server'
|
7
|
+
|
8
|
+
module Opener
|
9
|
+
class S3Outlet
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(input)
|
17
|
+
options[:text] = input
|
18
|
+
S3Output.create(options)
|
19
|
+
|
20
|
+
return input #Return original input so that we can keep on chaining.
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.s3
|
24
|
+
@s3 ||= AWS::S3.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.bucket
|
28
|
+
@bucket ||= s3.buckets[bucket_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.bucket_name
|
32
|
+
return "opener-outlet"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.dir
|
36
|
+
return "webservice"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
body {
|
2
|
+
font-family: arial;
|
3
|
+
line-height: 2em;
|
4
|
+
font-size: 15px;
|
5
|
+
margin-top: 3em;
|
6
|
+
}
|
7
|
+
|
8
|
+
.opener {
|
9
|
+
width: 800px;
|
10
|
+
margin-left: auto;
|
11
|
+
margin-right: auto;
|
12
|
+
}
|
13
|
+
|
14
|
+
.entities {
|
15
|
+
margin: 1em;
|
16
|
+
}
|
17
|
+
|
18
|
+
.entities:before {
|
19
|
+
font-size: 16px;
|
20
|
+
font-weight: bold;
|
21
|
+
content: "Entities";
|
22
|
+
}
|
23
|
+
|
24
|
+
.opinions {
|
25
|
+
margin: 1em;
|
26
|
+
}
|
27
|
+
|
28
|
+
.opinions:before {
|
29
|
+
font-size: 16px;
|
30
|
+
font-weight: bold;
|
31
|
+
content: "Opinions";
|
32
|
+
}
|
33
|
+
|
34
|
+
.properties {
|
35
|
+
margin: 1em;
|
36
|
+
}
|
37
|
+
|
38
|
+
.properties:before {
|
39
|
+
font-size: 16px;
|
40
|
+
font-weight: bold;
|
41
|
+
content: "Aspects";
|
42
|
+
}
|
43
|
+
|
44
|
+
.positive {
|
45
|
+
background-color: #AFA;
|
46
|
+
}
|
47
|
+
|
48
|
+
.negative {
|
49
|
+
background-color: #FAA;
|
50
|
+
}
|
51
|
+
|
52
|
+
.entity {
|
53
|
+
border-bottom: 2px dotted #00F;
|
54
|
+
}
|
55
|
+
|
56
|
+
.property {
|
57
|
+
border-bottom: 2px solid orange;
|
58
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
3
|
+
module Opener
|
4
|
+
class S3Outlet
|
5
|
+
class S3Output
|
6
|
+
#attr_accessor :params, :bucket
|
7
|
+
attr_reader :uuid, :text, :dir, :bucket, :params
|
8
|
+
|
9
|
+
def initialize(params = {})
|
10
|
+
@uuid = params.fetch(:uuid) { UUIDTools::UUID.random_create }
|
11
|
+
@text = params.fetch(:text)
|
12
|
+
@dir = params.fetch(:directory, S3Outlet.dir)
|
13
|
+
|
14
|
+
bucket = params[:bucket]
|
15
|
+
if bucket.kind_of?(String)
|
16
|
+
@bucket = S3Outlet.s3.buckets[bucket]
|
17
|
+
else
|
18
|
+
@bucket = bucket || S3Outlet.bucket
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def save
|
23
|
+
object = bucket.objects[filename]
|
24
|
+
object.write(text)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.find(uuid, dir=nil, bucket=nil)
|
28
|
+
filename = construct_filename(uuid, dir)
|
29
|
+
bucket = bucket || default_bucket
|
30
|
+
|
31
|
+
if bucket.objects[filename].exists?
|
32
|
+
file = bucket.objects[filename]
|
33
|
+
return file.read
|
34
|
+
else
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.create(params={})
|
40
|
+
new(params).save
|
41
|
+
end
|
42
|
+
|
43
|
+
def filename
|
44
|
+
self.class.construct_filename(uuid, dir)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def self.construct_filename(uuid, dir=nil)
|
50
|
+
dir = S3Outlet.dir if dir.nil? || dir.empty?
|
51
|
+
File.join(dir, "#{uuid}.kaf")
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.default_bucket
|
55
|
+
S3Outlet.bucket
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'stringio'
|
4
|
+
require_relative './visualizer'
|
5
|
+
|
6
|
+
require File.expand_path('../../../../config/aws', __FILE__)
|
7
|
+
|
8
|
+
module Opener
|
9
|
+
class S3Outlet
|
10
|
+
class Server < Sinatra::Base
|
11
|
+
|
12
|
+
post '/' do
|
13
|
+
output = S3Output.create(:uuid => params[:request_id], :text => params[:input])
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/' do
|
17
|
+
if params[:request_id]
|
18
|
+
redirect "#{url("/")}#{params[:request_id]}"
|
19
|
+
else
|
20
|
+
erb :index
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/:request_id' do
|
25
|
+
unless params[:request_id] == 'favicon.ico'
|
26
|
+
begin
|
27
|
+
output = S3Output.find(params[:request_id])
|
28
|
+
|
29
|
+
if output
|
30
|
+
content_type(:xml)
|
31
|
+
body(output)
|
32
|
+
else
|
33
|
+
halt(404, "No record found for ID #{params[:request_id]}")
|
34
|
+
end
|
35
|
+
rescue => error
|
36
|
+
error_callback = params[:error_callback]
|
37
|
+
|
38
|
+
submit_error(error_callback, error.message) if error_callback
|
39
|
+
|
40
|
+
raise(error)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
get '/html/:request_id' do
|
46
|
+
unless params[:request_id] == 'favicon.ico'
|
47
|
+
begin
|
48
|
+
output = S3Output.find(params[:request_id])
|
49
|
+
if output
|
50
|
+
output = StringIO.new(output)
|
51
|
+
parser = Opener::Kaf::Visualizer::Parser.new(output)
|
52
|
+
doc = parser.parse
|
53
|
+
html = Opener::Kaf::Visualizer::HTMLTextPresenter.new(doc)
|
54
|
+
@parsed = html.to_html
|
55
|
+
erb :show
|
56
|
+
else
|
57
|
+
halt(404, "No record found for ID #{params[:request_id]}")
|
58
|
+
end
|
59
|
+
rescue => error
|
60
|
+
error_callback = params[:error_callback]
|
61
|
+
|
62
|
+
submit_error(error_callback, error.message) if error_callback
|
63
|
+
|
64
|
+
raise(error)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def submit_error(url, message)
|
72
|
+
HTTPClient.post(url, :body => {:error => message})
|
73
|
+
end
|
74
|
+
end # Server
|
75
|
+
end # S3Outlet
|
76
|
+
end # Opener
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<link type="text/css" rel="stylesheet" charset="UTF-8" href="markdown.css"/>
|
5
|
+
<title>Opener S3 Outlet</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<h1>Opener S3 Outlet</h1>
|
9
|
+
|
10
|
+
<p>This Web Service, stores the output from a chain of Opener components in a
|
11
|
+
S3 bucket.</p>
|
12
|
+
<p>When using callbacks, the last Callback URL should be the URL that points
|
13
|
+
to S3 Outlet. Then, when you submit your text, you get a URL where the result
|
14
|
+
will be shown, when the processing is over.</p>
|
15
|
+
|
16
|
+
<h2>Search for an output</h2>
|
17
|
+
|
18
|
+
<p>* required</p>
|
19
|
+
|
20
|
+
|
21
|
+
<form action="<%=url("/")%>" method="GET">
|
22
|
+
<div>
|
23
|
+
<label for="request_id"/>Type the ID here*</label>
|
24
|
+
<br/>
|
25
|
+
|
26
|
+
<input type="text" name="request_id" id="text" rows="10" cols="50"/>
|
27
|
+
</div>
|
28
|
+
<input type="submit" value="Submit" />
|
29
|
+
</form>
|
30
|
+
|
31
|
+
</body>
|
32
|
+
</html>
|
33
|
+
|
@@ -0,0 +1,278 @@
|
|
1
|
+
#require "opener/kaf/visualizer/version"
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Opener
|
5
|
+
module Kaf
|
6
|
+
module Visualizer
|
7
|
+
class Parser
|
8
|
+
attr_reader :doc
|
9
|
+
attr_reader :words, :terms, :entities, :properties, :opinions, :document
|
10
|
+
|
11
|
+
def initialize(input_file_handler)
|
12
|
+
@doc = Nokogiri::XML(input_file_handler)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
@words = parse_words
|
17
|
+
@terms = parse_terms
|
18
|
+
@entities = parse_entities
|
19
|
+
@properties = parse_properties
|
20
|
+
@opinions = parse_opinions
|
21
|
+
@document = KAFDocument.new(
|
22
|
+
:words => words,
|
23
|
+
:terms => terms,
|
24
|
+
:entities => entities,
|
25
|
+
:properties => properties,
|
26
|
+
:opinions => opinions
|
27
|
+
)
|
28
|
+
|
29
|
+
return document
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_words
|
33
|
+
parse_elements("//wf", Word)
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_terms
|
37
|
+
# Of course terms should be words here.
|
38
|
+
# Dirty Hack, sufficient for now.
|
39
|
+
parse_elements("//term", Term, :terms=>words)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_entities
|
43
|
+
parse_elements("//entity", Entity, :terms=>terms)
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_properties
|
47
|
+
parse_elements("//property", Property, :terms=>terms)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_opinions
|
51
|
+
parse_elements("//opinion", Opinion, :terms=>terms)
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_elements(xpath, klass, opts={})
|
55
|
+
elements = doc.xpath(xpath)
|
56
|
+
lookup_table = Hash.new
|
57
|
+
elements.each do |element|
|
58
|
+
instance = klass.new(element, opts)
|
59
|
+
lookup_table[instance.id] = instance
|
60
|
+
end
|
61
|
+
lookup_table
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class KAFNode
|
67
|
+
attr_reader :content, :targets, :tag, :references
|
68
|
+
|
69
|
+
def initialize(tag, references)
|
70
|
+
@references = references
|
71
|
+
@tag = tag
|
72
|
+
|
73
|
+
set_instance_variables
|
74
|
+
set_content
|
75
|
+
set_targets
|
76
|
+
process_subnodes
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_content
|
80
|
+
@content = tag.content
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_instance_variables
|
84
|
+
tag.keys.each do |key|
|
85
|
+
if respond_to?("#{key}=".to_sym)
|
86
|
+
send("#{key}=".to_sym, tag[key])
|
87
|
+
else
|
88
|
+
instance_variable_set("@#{key}", tag[key])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_targets
|
94
|
+
@targets = []
|
95
|
+
tag.css("span target").each do |target|
|
96
|
+
id = target["id"]
|
97
|
+
@targets << references[:terms][id]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_subnodes
|
102
|
+
end
|
103
|
+
|
104
|
+
def has_target?(*ids)
|
105
|
+
ids.flatten.each do |id|
|
106
|
+
return true if target_ids.include?(id)
|
107
|
+
end
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
if targets.size > 0
|
113
|
+
return targets.map(&:to_s).join(" ")
|
114
|
+
else
|
115
|
+
return content
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def target_ids
|
120
|
+
@targets.map(&:id)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class Word < KAFNode
|
126
|
+
attr_reader :wid, :sent, :para, :offset
|
127
|
+
|
128
|
+
def id
|
129
|
+
wid
|
130
|
+
end
|
131
|
+
|
132
|
+
def offset=(offset)
|
133
|
+
@offset = offset.to_i
|
134
|
+
end
|
135
|
+
|
136
|
+
def length
|
137
|
+
content.nil? ? 0 : content.length
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
class Term < KAFNode
|
143
|
+
attr_reader :tid, :type, :lemma, :pos, :morphofeat
|
144
|
+
|
145
|
+
def id
|
146
|
+
tid
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Entity < KAFNode
|
151
|
+
attr_reader :eid, :type
|
152
|
+
|
153
|
+
def id
|
154
|
+
eid
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_s
|
158
|
+
"#{type}: #{targets.map(&:to_s).join(", ")}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Property < KAFNode
|
163
|
+
attr_reader :pid, :lemma
|
164
|
+
|
165
|
+
def id
|
166
|
+
pid
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_s
|
170
|
+
"#{lemma}: #{targets.map(&:to_s).join(", ")}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Opinion < KAFNode
|
175
|
+
attr_reader :oid, :expression
|
176
|
+
|
177
|
+
def id
|
178
|
+
oid
|
179
|
+
end
|
180
|
+
|
181
|
+
def process_subnodes
|
182
|
+
@expression = tag.xpath("opinion_expression").first["polarity"].to_sym
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_s
|
186
|
+
"#{expression}: #{targets.map(&:to_s).join(", ")}"
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class KAFDocument
|
192
|
+
attr_reader :words, :terms, :entities, :properties, :opinions
|
193
|
+
|
194
|
+
def initialize(opts={})
|
195
|
+
@words = opts.fetch(:words)
|
196
|
+
@terms = opts.fetch(:terms)
|
197
|
+
@entities = opts.fetch(:entities)
|
198
|
+
@properties = opts.fetch(:properties)
|
199
|
+
@opinions = opts.fetch(:opinions)
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
class HTMLTextPresenter
|
205
|
+
attr_reader :document
|
206
|
+
def initialize(document)
|
207
|
+
@document = document
|
208
|
+
end
|
209
|
+
|
210
|
+
def to_html
|
211
|
+
offset = 0
|
212
|
+
prev = Struct.new(:offset).new(0)
|
213
|
+
|
214
|
+
builder = Nokogiri::HTML::Builder.new do |html|
|
215
|
+
html.div(:class=>"opener") do
|
216
|
+
html.p do
|
217
|
+
document.words.values.sort_by(&:offset).each do |word|
|
218
|
+
if offset < word.offset
|
219
|
+
spacer = word.offset - offset
|
220
|
+
spacer = Array.new(spacer, " ").join
|
221
|
+
html.span(spacer)
|
222
|
+
end
|
223
|
+
|
224
|
+
terms = targets_for(:terms, word.id)
|
225
|
+
entities = targets_for(:entities, terms)
|
226
|
+
opinions = opinions_for(terms)
|
227
|
+
properties = targets_for(:properties, terms)
|
228
|
+
|
229
|
+
generics = []
|
230
|
+
generics << "term" if terms.size > 0
|
231
|
+
generics << "entity" if entities.size > 0
|
232
|
+
generics << "opinion" if opinions.size > 0
|
233
|
+
generics << "property" if properties.size > 0
|
234
|
+
|
235
|
+
classes = [terms, entities, opinions, properties, generics].flatten.uniq
|
236
|
+
|
237
|
+
word_annotations = classes.join(" ")
|
238
|
+
|
239
|
+
html.span(word.content, :class=>word_annotations, :id=>word.id)
|
240
|
+
offset = word.offset + word.length
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
[:entities, :opinions, :properties].each do |sym|
|
245
|
+
html.div(:class=>sym) do
|
246
|
+
document.public_send(sym).values.each do |entity|
|
247
|
+
html.div(entity.to_s, :id=>entity.id, :class=>entity.target_ids.join(" "))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
builder.to_html
|
255
|
+
end
|
256
|
+
|
257
|
+
def targets_for(variable, *ids)
|
258
|
+
targets = document.public_send(variable.to_sym).values.select do |value|
|
259
|
+
value.has_target?(ids.flatten)
|
260
|
+
end
|
261
|
+
|
262
|
+
targets.map(&:id)
|
263
|
+
end
|
264
|
+
|
265
|
+
def opinions_for(*ids)
|
266
|
+
targets = document.opinions.values.select do |value|
|
267
|
+
value.has_target?(ids.flatten)
|
268
|
+
end
|
269
|
+
|
270
|
+
ids = targets.map(&:id)
|
271
|
+
sentiments = targets.map(&:expression)
|
272
|
+
return ids.concat(sentiments).uniq
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path('../lib/opener/s3_outlet/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'opener-s3-outlet'
|
5
|
+
gem.version = Opener::S3Outlet::VERSION
|
6
|
+
gem.authors = ['development@olery.com']
|
7
|
+
gem.summary = 'S3 Bucket data storing for the web services output when using callbacks.'
|
8
|
+
gem.description = gem.summary
|
9
|
+
gem.homepage = "http://opener-project.github.com/"
|
10
|
+
gem.has_rdoc = 'yard'
|
11
|
+
gem.required_ruby_version = '>= 1.9.2'
|
12
|
+
|
13
|
+
gem.files = Dir.glob([
|
14
|
+
'config/**/*',
|
15
|
+
'exec/**/*',
|
16
|
+
'lib/**/*',
|
17
|
+
'config.ru',
|
18
|
+
'*.gemspec',
|
19
|
+
'README.md',
|
20
|
+
'visualizer.rb'
|
21
|
+
]).select { |file| File.file?(file) }
|
22
|
+
|
23
|
+
gem.executables = Dir.glob('bin/*').map { |file| File.basename(file) }
|
24
|
+
|
25
|
+
gem.add_dependency 'builder'
|
26
|
+
gem.add_dependency 'sinatra', '~>1.4.2'
|
27
|
+
gem.add_dependency 'nokogiri'
|
28
|
+
gem.add_dependency 'httpclient'
|
29
|
+
gem.add_dependency 'uuidtools'
|
30
|
+
gem.add_dependency 'opener-webservice'
|
31
|
+
gem.add_dependency 'opener-daemons'
|
32
|
+
gem.add_dependency 'aws-sdk'
|
33
|
+
|
34
|
+
gem.add_development_dependency 'rspec'
|
35
|
+
gem.add_development_dependency 'cucumber'
|
36
|
+
gem.add_development_dependency 'pry'
|
37
|
+
gem.add_development_dependency 'rake'
|
38
|
+
end
|
data/visualizer.rb
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Opener
|
4
|
+
module Kaf
|
5
|
+
module Visualizer
|
6
|
+
class Parser
|
7
|
+
attr_reader :doc
|
8
|
+
attr_reader :words, :terms, :entities, :properties, :opinions, :document
|
9
|
+
|
10
|
+
def initialize(input_file_handler)
|
11
|
+
@doc = Nokogiri::XML(input_file_handler)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse
|
15
|
+
@words = parse_words
|
16
|
+
@terms = parse_terms
|
17
|
+
@entities = parse_entities
|
18
|
+
@properties = parse_properties
|
19
|
+
@opinions = parse_opinions
|
20
|
+
@document = KAFDocument.new(
|
21
|
+
:words => words,
|
22
|
+
:terms => terms,
|
23
|
+
:entities => entities,
|
24
|
+
:properties => properties,
|
25
|
+
:opinions => opinions
|
26
|
+
)
|
27
|
+
|
28
|
+
return document
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_words
|
32
|
+
parse_elements("//wf", Word)
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_terms
|
36
|
+
# Of course terms should be words here.
|
37
|
+
# Dirty Hack, sufficient for now.
|
38
|
+
parse_elements("//term", Term, :terms=>words)
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_entities
|
42
|
+
parse_elements("//entity", Entity, :terms=>terms)
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_properties
|
46
|
+
parse_elements("//property", Property, :terms=>terms)
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_opinions
|
50
|
+
parse_elements("//opinion", Opinion, :terms=>terms)
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_elements(xpath, klass, opts={})
|
54
|
+
elements = doc.xpath(xpath)
|
55
|
+
lookup_table = Hash.new
|
56
|
+
elements.each do |element|
|
57
|
+
instance = klass.new(element, opts)
|
58
|
+
lookup_table[instance.id] = instance
|
59
|
+
end
|
60
|
+
lookup_table
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class KAFNode
|
66
|
+
attr_reader :content, :targets, :tag, :references
|
67
|
+
|
68
|
+
def initialize(tag, references)
|
69
|
+
@references = references
|
70
|
+
@tag = tag
|
71
|
+
|
72
|
+
set_instance_variables
|
73
|
+
set_content
|
74
|
+
set_targets
|
75
|
+
process_subnodes
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_content
|
79
|
+
@content = tag.content
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_instance_variables
|
83
|
+
tag.keys.each do |key|
|
84
|
+
if respond_to?("#{key}=".to_sym)
|
85
|
+
send("#{key}=".to_sym, tag[key])
|
86
|
+
else
|
87
|
+
instance_variable_set("@#{key}", tag[key])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_targets
|
93
|
+
@targets = []
|
94
|
+
tag.css("span target").each do |target|
|
95
|
+
id = target["id"]
|
96
|
+
@targets << references[:terms][id]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def process_subnodes
|
101
|
+
end
|
102
|
+
|
103
|
+
def has_target?(*ids)
|
104
|
+
ids.flatten.each do |id|
|
105
|
+
return true if target_ids.include?(id)
|
106
|
+
end
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
if targets.size > 0
|
112
|
+
return targets.map(&:to_s).join(" ")
|
113
|
+
else
|
114
|
+
return content
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def target_ids
|
119
|
+
@targets.map(&:id)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
class Word < KAFNode
|
125
|
+
attr_reader :wid, :sent, :para, :offset
|
126
|
+
|
127
|
+
def id
|
128
|
+
wid
|
129
|
+
end
|
130
|
+
|
131
|
+
def offset=(offset)
|
132
|
+
@offset = offset.to_i
|
133
|
+
end
|
134
|
+
|
135
|
+
def length
|
136
|
+
content.nil? ? 0 : content.length
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
class Term < KAFNode
|
142
|
+
attr_reader :tid, :type, :lemma, :pos, :morphofeat
|
143
|
+
|
144
|
+
def id
|
145
|
+
tid
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Entity < KAFNode
|
150
|
+
attr_reader :eid, :type
|
151
|
+
|
152
|
+
def id
|
153
|
+
eid
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
"#{type}: #{targets.map(&:to_s).join(", ")}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class Property < KAFNode
|
162
|
+
attr_reader :pid, :lemma
|
163
|
+
|
164
|
+
def id
|
165
|
+
pid
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_s
|
169
|
+
"#{lemma}: #{targets.map(&:to_s).join(", ")}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Opinion < KAFNode
|
174
|
+
attr_reader :oid, :expression
|
175
|
+
|
176
|
+
def id
|
177
|
+
oid
|
178
|
+
end
|
179
|
+
|
180
|
+
def process_subnodes
|
181
|
+
@expression = tag.xpath("//opinion_expression").first["polarity"].to_sym
|
182
|
+
end
|
183
|
+
|
184
|
+
def to_s
|
185
|
+
"#{expression}: #{targets.map(&:to_s).join(", ")}"
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
class KAFDocument
|
191
|
+
attr_reader :words, :terms, :entities, :properties, :opinions
|
192
|
+
|
193
|
+
def initialize(opts={})
|
194
|
+
@words = opts.fetch(:words)
|
195
|
+
@terms = opts.fetch(:terms)
|
196
|
+
@entities = opts.fetch(:entities)
|
197
|
+
@properties = opts.fetch(:properties)
|
198
|
+
@opinions = opts.fetch(:opinions)
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
class HTMLTextPresenter
|
204
|
+
attr_reader :document
|
205
|
+
def initialize(document)
|
206
|
+
@document = document
|
207
|
+
end
|
208
|
+
|
209
|
+
def to_html
|
210
|
+
offset = 0
|
211
|
+
prev = Struct.new(:offset).new(0)
|
212
|
+
|
213
|
+
builder = Nokogiri::HTML::Builder.new do |html|
|
214
|
+
html.div(:class=>"opener") do
|
215
|
+
html.p do
|
216
|
+
document.words.values.sort_by(&:offset).each do |word|
|
217
|
+
if offset < word.offset
|
218
|
+
spacer = word.offset - offset
|
219
|
+
spacer = Array.new(spacer, " ").join
|
220
|
+
html.span(spacer)
|
221
|
+
end
|
222
|
+
|
223
|
+
terms = targets_for(:terms, word.id)
|
224
|
+
entities = targets_for(:entities, terms)
|
225
|
+
opinions = opinions_for(terms)
|
226
|
+
properties = targets_for(:properties, terms)
|
227
|
+
|
228
|
+
generics = []
|
229
|
+
generics << "term" if terms.size > 0
|
230
|
+
generics << "entity" if entities.size > 0
|
231
|
+
generics << "opinion" if opinions.size > 0
|
232
|
+
generics << "property" if properties.size > 0
|
233
|
+
|
234
|
+
classes = [terms, entities, opinions, properties, generics].flatten.uniq
|
235
|
+
|
236
|
+
word_annotations = classes.join(" ")
|
237
|
+
|
238
|
+
html.span(word.content, :class=>word_annotations, :id=>word.id)
|
239
|
+
offset = word.offset + word.length
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
[:entities, :opinions, :properties].each do |sym|
|
244
|
+
html.div(:class=>sym) do
|
245
|
+
document.public_send(sym).values.each do |entity|
|
246
|
+
html.div(entity.to_s, :id=>entity.id, :class=>entity.target_ids.join(" "))
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
builder.to_html
|
254
|
+
end
|
255
|
+
|
256
|
+
def targets_for(variable, *ids)
|
257
|
+
targets = document.public_send(variable.to_sym).values.select do |value|
|
258
|
+
value.has_target?(ids.flatten)
|
259
|
+
end
|
260
|
+
|
261
|
+
targets.map(&:id)
|
262
|
+
end
|
263
|
+
|
264
|
+
def opinions_for(*ids)
|
265
|
+
targets = document.opinions.values.select do |value|
|
266
|
+
value.has_target?(ids.flatten)
|
267
|
+
end
|
268
|
+
|
269
|
+
ids = targets.map(&:id)
|
270
|
+
sentiments = targets.map(&:expression)
|
271
|
+
return ids.concat(sentiments).uniq
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
metadata
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: opener-s3-outlet
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- development@olery.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: builder
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.4.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: httpclient
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: uuidtools
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: opener-webservice
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: opener-daemons
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: aws-sdk
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: cucumber
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rake
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: S3 Bucket data storing for the web services output when using callbacks.
|
182
|
+
email:
|
183
|
+
executables:
|
184
|
+
- s3-outlet-daemon
|
185
|
+
- s3-outlet-server
|
186
|
+
extensions: []
|
187
|
+
extra_rdoc_files: []
|
188
|
+
files:
|
189
|
+
- README.md
|
190
|
+
- bin/s3-outlet-daemon
|
191
|
+
- bin/s3-outlet-server
|
192
|
+
- config.ru
|
193
|
+
- config/aws.rb
|
194
|
+
- exec/s3-outlet.rb
|
195
|
+
- lib/opener/s3_outlet.rb
|
196
|
+
- lib/opener/s3_outlet/public/outlet.css
|
197
|
+
- lib/opener/s3_outlet/s3_output.rb
|
198
|
+
- lib/opener/s3_outlet/server.rb
|
199
|
+
- lib/opener/s3_outlet/version.rb
|
200
|
+
- lib/opener/s3_outlet/views/index.erb
|
201
|
+
- lib/opener/s3_outlet/views/show.erb
|
202
|
+
- lib/opener/s3_outlet/views/show.html.erb
|
203
|
+
- lib/opener/s3_outlet/visualizer.rb
|
204
|
+
- opener-s3-outlet.gemspec
|
205
|
+
- visualizer.rb
|
206
|
+
homepage: http://opener-project.github.com/
|
207
|
+
licenses: []
|
208
|
+
metadata: {}
|
209
|
+
post_install_message:
|
210
|
+
rdoc_options: []
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: 1.9.2
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubyforge_project:
|
225
|
+
rubygems_version: 2.2.2
|
226
|
+
signing_key:
|
227
|
+
specification_version: 4
|
228
|
+
summary: S3 Bucket data storing for the web services output when using callbacks.
|
229
|
+
test_files: []
|
230
|
+
has_rdoc: yard
|