opener-opinion-detector 1.0.0

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: 04c5af868b17a2e60d8a4ce8f5bfbd32842129fa
4
+ data.tar.gz: 0348514671da532342f41d71011b226705cadb14
5
+ SHA512:
6
+ metadata.gz: 633377edd6cf4606e8e24841a8de9ca654008b8c8e6eda5a97c0b564d08969e916e9370c73d25e045ac2baff415021a37534b1c7c81933d05bfa21599e18b49d
7
+ data.tar.gz: d58166aa6337bc845cb0d542cd7810829e26a2d874542978c39cb7efe8889bd41c6e31428bb7392dfd6ea3a8d603d128d7babe95c5440f38464a79329acf9c2a
data/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # OpinionDetector
2
+
3
+ Component that wraps the different existing Opinion Detectors.
4
+
5
+ ## Requirements
6
+
7
+ * Perl 5
8
+ * Ruby 1.9.2 or newer
9
+ * Make
10
+
11
+ ## Developers
12
+
13
+ See how to edit / change / compile this gem at the bottom of this file.
14
+
15
+ ## Installation
16
+
17
+ ### As part of a Gemfile in a Ruby application
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ gem 'opener-opinion-detector',
22
+ :git=>"git@github.com:opener-project/opinion-detector.git"
23
+
24
+ And then execute:
25
+
26
+ $ bundle install
27
+
28
+ ### As a standalone GEM:
29
+
30
+ Make sure you have the ```specific_install``` gem installed first by running
31
+
32
+ $ gem install specific_install
33
+
34
+ After that you can install the gem from the git repository like this:
35
+
36
+ $ gem specific_install opener-opinion-detector \
37
+ -l https://github.com/opener-project/opinion-detector.git
38
+
39
+ Once the gem is installed you have access to the following command from
40
+ anywhere on your computer:
41
+
42
+ $ cat englist.kaf | opinion-detector
43
+
44
+ or you can launch a webservice with
45
+
46
+ $ opinion-detector-server
47
+
48
+ KAF file text:
49
+
50
+ <?xml version='1.0' encoding='UTF-8'?>
51
+ <KAF xml:lang="en">
52
+ <kafHeader>
53
+ <linguisticProcessors layer="text">
54
+ <lp timestamp="2013-04-11T09:21:29CEST" version="1.0" name="Open-nlp sentence splitter"/>
55
+ <lp timestamp="2013-04-11T09:21:29CEST" version="1.0" name="Open-nlp tokenizer"/>
56
+ </linguisticProcessors>
57
+ <linguisticProcessor layer="terms">
58
+ <lp timestamp="2013-04-11T09:23:08CEST" version="1.0" name="Basic_polarity_tagger_with_pos"/>
59
+ </linguisticProcessor>
60
+ <linguisticProcessor layer="term">
61
+ <lp timestamp="2013-04-11T09:23:07CEST" version="1.0" name="TreeTagger_from_kaf"/>
62
+ </linguisticProcessor>
63
+ </kafHeader>
64
+ <text>
65
+ <wf wid="w_1" sent="s_1">This</wf>
66
+ <wf wid="w_2" sent="s_1">is</wf>
67
+ <wf wid="w_3" sent="s_1">a</wf>
68
+ <wf wid="w_4" sent="s_1">very</wf>
69
+ <wf wid="w_5" sent="s_1">nice</wf>
70
+ <wf wid="w_6" sent="s_1">hotel</wf>
71
+ <wf wid="w_7" sent="s_1">in</wf>
72
+ <wf wid="w_8" sent="s_1">Amsterdam</wf>
73
+ </text>
74
+ <terms>
75
+ <term tid="t_1" lemma="this" morphofeat="DT" type="close" pos="D">
76
+ <span>
77
+ <target id="w_1"/>
78
+ </span>
79
+ </term>
80
+ <term tid="t_2" lemma="be" morphofeat="VBZ" type="open" pos="V">
81
+ <span>
82
+ <target id="w_2"/>
83
+ </span>
84
+ </term>
85
+ <term tid="t_3" lemma="a" morphofeat="DT" type="close" pos="D">
86
+ <span>
87
+ <target id="w_3"/>
88
+ </span>
89
+ </term>
90
+ <term tid="t_4" lemma="very" morphofeat="RB" type="open" pos="A">
91
+ <span>
92
+ <target id="w_4"/>
93
+ </span>
94
+ <sentiment sentiment_modifier="intensifier" resource="VUA_olery_lexicon_en_lmf"/>
95
+ </term>
96
+ <term tid="t_5" lemma="nice" morphofeat="JJ" type="open" pos="G">
97
+ <span>
98
+ <target id="w_5"/>
99
+ </span>
100
+ <sentiment polarity="positive" resource="VUA_olery_lexicon_en_lmf"/>
101
+ </term>
102
+ <term tid="t_6" lemma="hotel" morphofeat="NN" type="open" pos="N">
103
+ <span>
104
+ <target id="w_6"/>
105
+ </span>
106
+ </term>
107
+ <term tid="t_7" lemma="in" morphofeat="IN" type="close" pos="P">
108
+ <span>
109
+ <target id="w_7"/>
110
+ </span>
111
+ </term>
112
+ <term tid="t_8" lemma="Amsterdam" morphofeat="NP" type="open" pos="R">
113
+ <span>
114
+ <target id="w_8"/>
115
+ </span>
116
+ </term>
117
+ </terms>
118
+ <entities>
119
+ <entity eid="e15" type="location">
120
+ <references>
121
+ <span>
122
+ <!--Amsterdam -->
123
+ <target id="t_8" />
124
+ </span>
125
+ </references>
126
+ </entity>
127
+ </entities>
128
+ </KAF>
129
+
130
+
131
+ Enjoy!
132
+
133
+ ## Usage
134
+
135
+ Detecting opinion for some text (assuming that the above text is in a file called *english.kaf*):
136
+
137
+ cat english.kaf | opinion-detector
138
+
139
+
140
+ Will result in
141
+
142
+ <?xml version='1.0' encoding='UTF-8'?>
143
+ <KAF version="v1.opener" xml:lang="en">
144
+ <kafHeader>
145
+ <linguisticProcessors layer="text">
146
+ <lp name="opennlp-en-tok" timestamp="2013-06-11T13:41:37Z" version="1.0"/>
147
+ <lp name="opennlp-en-sent" timestamp="2013-06-11T13:41:37Z" version="1.0"/>
148
+ </linguisticProcessors>
149
+ <linguisticProcessor layer="term">
150
+ <lp timestamp="2013-06-12T15:18:03CEST" version="1.0" name="Open nlp pos tagger"/>
151
+ </linguisticProcessor>
152
+ </kafHeader>
153
+ <text>
154
+ <wf length="4" offset="0" para="1" sent="1" wid="w1">this</wf>
155
+ <wf length="2" offset="5" para="1" sent="1" wid="w2">is</wf>
156
+ <wf length="2" offset="8" para="1" sent="1" wid="w3">an</wf>
157
+ <wf length="7" offset="11" para="1" sent="1" wid="w4">english</wf>
158
+ <wf length="4" offset="19" para="1" sent="1" wid="w5">text</wf>
159
+ </text>
160
+ <terms>
161
+ <term lemma="this" morphofeat="FM" pos="O" tid="t_1" type="open">
162
+ <span>
163
+ <target id="w1"/>
164
+ </span>
165
+ </term>
166
+ <term lemma="is" morphofeat="FM" pos="O" tid="t_2" type="open">
167
+ <span>
168
+ <target id="w2"/>
169
+ </span>
170
+ </term>
171
+ <term lemma="an" morphofeat="APPR" pos="P" tid="t_3" type="close">
172
+ <span>
173
+ <target id="w3"/>
174
+ </span>
175
+ </term>
176
+ <term lemma="english" morphofeat="FM" pos="O" tid="t_4" type="open">
177
+ <span>
178
+ <target id="w4"/>
179
+ </span>
180
+ </term>
181
+ <term lemma="text" morphofeat="FM" pos="O" tid="t_5" type="open">
182
+ <span>
183
+ <target id="w5"/>
184
+ </span>
185
+ </term>
186
+ </terms>
187
+ </KAF>
188
+
189
+ ## Server
190
+
191
+ The Opinion Detector comes equipped with a simple webservice. To start the
192
+ webservice type:
193
+
194
+ opinion-detector-server
195
+
196
+ This will launch a mini webserver with the webservice. It defaults to port 9292,
197
+ so you can access it at:
198
+
199
+ http://localhost:9292
200
+
201
+ To launch it on a different port provide the ```-p [port-number]``` option like
202
+ this:
203
+
204
+ opinion-detector-server -p 1234
205
+
206
+ It then launches at ```http://localhost:1234```
207
+
208
+ Documentation on the Webservice is provided by surfing to the urls provided
209
+ above.
210
+
211
+
212
+ ## Contributing
213
+
214
+ ### Procedure
215
+
216
+ 1. Pull it
217
+ 2. Create your feature branch (`git checkout -b features/my-new-feature`)
218
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
219
+ 4. Push to the branch (`git push origin features/my-new-feature`)
220
+ 5. If you're confident, merge your changes into master.
221
+
222
+ # What's next?
223
+
224
+ If you're interested in the pos_tagger, you also might want to check
225
+ out opener-project/opinion-detector.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/opener/opinion_detector'
3
+
4
+ cli = Opener::OpinionDetector::CLI.new(:args => ARGV)
5
+
6
+ cli.run(STDIN.tty? ? nil : STDIN.read)
@@ -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
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../lib/opener/opinion_detector', __FILE__)
2
+ require File.expand_path('../lib/opener/opinion_detector/server', __FILE__)
3
+
4
+ run Opener::OpinionDetector::Server
@@ -0,0 +1,90 @@
1
+ require 'opener/opinion_detectors/base'
2
+ require 'open3'
3
+ require 'nokogiri'
4
+ require 'optparse'
5
+
6
+ require_relative 'opinion_detector/version'
7
+ require_relative 'opinion_detector/cli'
8
+
9
+ module Opener
10
+ ##
11
+ # Primary opinion detector class that delegates work to the various opinion
12
+ # detector kernels.
13
+ #
14
+ # @!attribute [r] options
15
+ # @return [Hash]
16
+ #
17
+ class OpinionDetector
18
+ attr_reader :options
19
+
20
+ ##
21
+ # Hash containing the default options to use.
22
+ #
23
+ # @return [Hash]
24
+ #
25
+ DEFAULT_OPTIONS = {
26
+ :args => []
27
+ }.freeze
28
+
29
+ ##
30
+ # @param [Hash] options
31
+ #
32
+ # @option options [Array] :args Collection of arbitrary arguments to pass
33
+ # to the individual tokenizer commands.
34
+ #
35
+ def initialize(options = {})
36
+ @options = DEFAULT_OPTIONS.merge(options)
37
+ end
38
+
39
+ ##
40
+ # Processes the input and returns an array containing the output of STDOUT,
41
+ # STDERR and an object containing process information.
42
+ #
43
+ # @param [String] input
44
+ # @return [Array]
45
+ #
46
+ def run(input)
47
+ language = language_from_kaf(input)
48
+
49
+ unless valid_language?(language)
50
+ raise ArgumentError, "The specified language (#{language}) is invalid"
51
+ end
52
+
53
+ kernel = language_constant(language).new(:args => options[:args])
54
+ return kernel.run(input)
55
+ end
56
+
57
+ protected
58
+
59
+ ##
60
+ # Extracts the language from a KAF document.
61
+ #
62
+ # @param [String] input
63
+ # @return [String]
64
+ #
65
+ def language_from_kaf(input)
66
+ reader = Nokogiri::XML::Reader(input)
67
+
68
+ return reader.read.lang
69
+ end
70
+
71
+ ##
72
+ # @param [String] language
73
+ # @return [Class]
74
+ #
75
+ def language_constant(language)
76
+ Opener::OpinionDetectors.const_get(language.upcase)
77
+ end
78
+
79
+ ##
80
+ # @return [TrueClass|FalseClass]
81
+ #
82
+ def valid_language?(language)
83
+ if language
84
+ return Opener::OpinionDetectors.const_defined?(language.upcase)
85
+ else
86
+ return false
87
+ end
88
+ end
89
+ end # OpinionDetector
90
+ end # Opener
@@ -0,0 +1,84 @@
1
+ module Opener
2
+ class OpinionDetector
3
+ ##
4
+ # CLI wrapper around {Opener::OpinionDetector} using OptionParser.
5
+ #
6
+ # @!attribute [r] options
7
+ # @return [Hash]
8
+ # @!attribute [r] option_parser
9
+ # @return [OptionParser]
10
+ #
11
+ class CLI
12
+ attr_reader :options, :option_parser
13
+
14
+ ##
15
+ # @param [Hash] options
16
+ #
17
+ def initialize(options = {})
18
+ @options = DEFAULT_OPTIONS.merge(options)
19
+
20
+ @option_parser = OptionParser.new do |opts|
21
+ opts.program_name = 'opinion-detector'
22
+ opts.summary_indent = ' '
23
+
24
+ opts.on('-h', '--help', 'Shows this help message') do
25
+ show_help
26
+ end
27
+
28
+ opts.on('-v', '--version', 'Shows the current version') do
29
+ show_version
30
+ end
31
+
32
+ opts.on('-l', '--log', 'Enables logging to STDERR') do
33
+ @options[:logging] = true
34
+ end
35
+
36
+ opts.separator <<-EOF
37
+
38
+ Examples:
39
+
40
+ cat example.kaf | #{opts.program_name}
41
+ cat example.kaf | #{opts.program_name} -l # Enables logging to STDERR
42
+ EOF
43
+ end
44
+ end
45
+
46
+ ##
47
+ # @param [String] input
48
+ #
49
+ def run(input)
50
+ option_parser.parse!(options[:args])
51
+
52
+ tagger = OpinionDetector.new(options)
53
+
54
+ stdout, stderr, process = tagger.run(input)
55
+
56
+ if process.success?
57
+ puts stdout
58
+
59
+ if options[:logging] and !stderr.empty?
60
+ STDERR.puts(stderr)
61
+ end
62
+ else
63
+ abort stderr
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ ##
70
+ # Shows the help message and exits the program.
71
+ #
72
+ def show_help
73
+ abort option_parser.to_s
74
+ end
75
+
76
+ ##
77
+ # Shows the version and exits the program.
78
+ #
79
+ def show_version
80
+ abort "#{option_parser.program_name} v#{VERSION} on #{RUBY_DESCRIPTION}"
81
+ end
82
+ end # CLI
83
+ end # OpinionDetector
84
+ end # Opener
@@ -0,0 +1,283 @@
1
+ input[type="text"], textarea
2
+ {
3
+ width: 500px;
4
+ }
5
+
6
+ body {
7
+ font-family: Helvetica, arial, sans-serif;
8
+ font-size: 14px;
9
+ line-height: 1.6;
10
+ padding-top: 10px;
11
+ padding-bottom: 10px;
12
+ background-color: white;
13
+ padding: 30px; }
14
+
15
+ body > *:first-child {
16
+ margin-top: 0 !important; }
17
+ body > *:last-child {
18
+ margin-bottom: 0 !important; }
19
+
20
+ a {
21
+ color: #4183C4; }
22
+ a.absent {
23
+ color: #cc0000; }
24
+ a.anchor {
25
+ display: block;
26
+ padding-left: 30px;
27
+ margin-left: -30px;
28
+ cursor: pointer;
29
+ position: absolute;
30
+ top: 0;
31
+ left: 0;
32
+ bottom: 0; }
33
+
34
+ h1, h2, h3, h4, h5, h6 {
35
+ margin: 20px 0 10px;
36
+ padding: 0;
37
+ font-weight: bold;
38
+ -webkit-font-smoothing: antialiased;
39
+ cursor: text;
40
+ position: relative; }
41
+
42
+ h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
43
+ background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;
44
+ text-decoration: none; }
45
+
46
+ h1 tt, h1 code {
47
+ font-size: inherit; }
48
+
49
+ h2 tt, h2 code {
50
+ font-size: inherit; }
51
+
52
+ h3 tt, h3 code {
53
+ font-size: inherit; }
54
+
55
+ h4 tt, h4 code {
56
+ font-size: inherit; }
57
+
58
+ h5 tt, h5 code {
59
+ font-size: inherit; }
60
+
61
+ h6 tt, h6 code {
62
+ font-size: inherit; }
63
+
64
+ h1 {
65
+ font-size: 28px;
66
+ color: black; }
67
+
68
+ h2 {
69
+ font-size: 24px;
70
+ border-bottom: 1px solid #cccccc;
71
+ color: black; }
72
+
73
+ h3 {
74
+ font-size: 18px; }
75
+
76
+ h4 {
77
+ font-size: 16px; }
78
+
79
+ h5 {
80
+ font-size: 14px; }
81
+
82
+ h6 {
83
+ color: #777777;
84
+ font-size: 14px; }
85
+
86
+ p, blockquote, ul, ol, dl, li, table, pre {
87
+ margin: 15px 0; }
88
+
89
+ hr {
90
+ background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0;
91
+ border: 0 none;
92
+ color: #cccccc;
93
+ height: 4px;
94
+ padding: 0; }
95
+
96
+ body > h2:first-child {
97
+ margin-top: 0;
98
+ padding-top: 0; }
99
+ body > h1:first-child {
100
+ margin-top: 0;
101
+ padding-top: 0; }
102
+ body > h1:first-child + h2 {
103
+ margin-top: 0;
104
+ padding-top: 0; }
105
+ body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
106
+ margin-top: 0;
107
+ padding-top: 0; }
108
+
109
+ a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
110
+ margin-top: 0;
111
+ padding-top: 0; }
112
+
113
+ h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
114
+ margin-top: 0; }
115
+
116
+ li p.first {
117
+ display: inline-block; }
118
+
119
+ ul, ol {
120
+ padding-left: 30px; }
121
+
122
+ ul :first-child, ol :first-child {
123
+ margin-top: 0; }
124
+
125
+ ul :last-child, ol :last-child {
126
+ margin-bottom: 0; }
127
+
128
+ dl {
129
+ padding: 0; }
130
+ dl dt {
131
+ font-size: 14px;
132
+ font-weight: bold;
133
+ font-style: italic;
134
+ padding: 0;
135
+ margin: 15px 0 5px; }
136
+ dl dt:first-child {
137
+ padding: 0; }
138
+ dl dt > :first-child {
139
+ margin-top: 0; }
140
+ dl dt > :last-child {
141
+ margin-bottom: 0; }
142
+ dl dd {
143
+ margin: 0 0 15px;
144
+ padding: 0 15px; }
145
+ dl dd > :first-child {
146
+ margin-top: 0; }
147
+ dl dd > :last-child {
148
+ margin-bottom: 0; }
149
+
150
+ blockquote {
151
+ border-left: 4px solid #dddddd;
152
+ padding: 0 15px;
153
+ color: #777777; }
154
+ blockquote > :first-child {
155
+ margin-top: 0; }
156
+ blockquote > :last-child {
157
+ margin-bottom: 0; }
158
+
159
+ table {
160
+ padding: 0; }
161
+ table tr {
162
+ border-top: 1px solid #cccccc;
163
+ background-color: white;
164
+ margin: 0;
165
+ padding: 0; }
166
+ table tr:nth-child(2n) {
167
+ background-color: #f8f8f8; }
168
+ table tr th {
169
+ font-weight: bold;
170
+ border: 1px solid #cccccc;
171
+ text-align: left;
172
+ margin: 0;
173
+ padding: 6px 13px; }
174
+ table tr td {
175
+ border: 1px solid #cccccc;
176
+ text-align: left;
177
+ margin: 0;
178
+ padding: 6px 13px; }
179
+ table tr th :first-child, table tr td :first-child {
180
+ margin-top: 0; }
181
+ table tr th :last-child, table tr td :last-child {
182
+ margin-bottom: 0; }
183
+
184
+ img {
185
+ max-width: 100%; }
186
+
187
+ span.frame {
188
+ display: block;
189
+ overflow: hidden; }
190
+ span.frame > span {
191
+ border: 1px solid #dddddd;
192
+ display: block;
193
+ float: left;
194
+ overflow: hidden;
195
+ margin: 13px 0 0;
196
+ padding: 7px;
197
+ width: auto; }
198
+ span.frame span img {
199
+ display: block;
200
+ float: left; }
201
+ span.frame span span {
202
+ clear: both;
203
+ color: #333333;
204
+ display: block;
205
+ padding: 5px 0 0; }
206
+ span.align-center {
207
+ display: block;
208
+ overflow: hidden;
209
+ clear: both; }
210
+ span.align-center > span {
211
+ display: block;
212
+ overflow: hidden;
213
+ margin: 13px auto 0;
214
+ text-align: center; }
215
+ span.align-center span img {
216
+ margin: 0 auto;
217
+ text-align: center; }
218
+ span.align-right {
219
+ display: block;
220
+ overflow: hidden;
221
+ clear: both; }
222
+ span.align-right > span {
223
+ display: block;
224
+ overflow: hidden;
225
+ margin: 13px 0 0;
226
+ text-align: right; }
227
+ span.align-right span img {
228
+ margin: 0;
229
+ text-align: right; }
230
+ span.float-left {
231
+ display: block;
232
+ margin-right: 13px;
233
+ overflow: hidden;
234
+ float: left; }
235
+ span.float-left span {
236
+ margin: 13px 0 0; }
237
+ span.float-right {
238
+ display: block;
239
+ margin-left: 13px;
240
+ overflow: hidden;
241
+ float: right; }
242
+ span.float-right > span {
243
+ display: block;
244
+ overflow: hidden;
245
+ margin: 13px auto 0;
246
+ text-align: right; }
247
+
248
+ code, tt {
249
+ margin: 0 2px;
250
+ padding: 0 5px;
251
+ white-space: nowrap;
252
+ border: 1px solid #eaeaea;
253
+ background-color: #f8f8f8;
254
+ border-radius: 3px; }
255
+
256
+ pre code {
257
+ margin: 0;
258
+ padding: 0;
259
+ white-space: pre;
260
+ border: none;
261
+ background: transparent; }
262
+
263
+ .highlight pre {
264
+ background-color: #f8f8f8;
265
+ border: 1px solid #cccccc;
266
+ font-size: 13px;
267
+ line-height: 19px;
268
+ overflow: auto;
269
+ padding: 6px 10px;
270
+ border-radius: 3px; }
271
+
272
+ pre {
273
+ background-color: #f8f8f8;
274
+ border: 1px solid #cccccc;
275
+ font-size: 13px;
276
+ line-height: 19px;
277
+ overflow: auto;
278
+ padding: 6px 10px;
279
+ border-radius: 3px; }
280
+ pre code, pre tt {
281
+ background-color: transparent;
282
+ border: none; }
283
+
@@ -0,0 +1,29 @@
1
+ require 'sinatra/base'
2
+ require 'httpclient'
3
+ require 'opener/webservice'
4
+
5
+ module Opener
6
+ class OpinionDetector
7
+ ##
8
+ # Opinion Detector server powered by Sinatra.
9
+ #
10
+ class Server < Webservice
11
+ set :views, File.expand_path('../views', __FILE__)
12
+ text_processor OpinionDetector
13
+ accepted_params :input
14
+
15
+ ##
16
+ # @see Opener::Webservice#analyze
17
+ #
18
+ def analyze(*args)
19
+ begin
20
+ super
21
+ # ArgumentErrors are used for invalid languages. These happen too often
22
+ # so we'll supress them for now.
23
+ rescue ArgumentError => error
24
+ halt(400, error.message.strip)
25
+ end
26
+ end
27
+ end # Server
28
+ end # OpinionDetector
29
+ end # Opener
@@ -0,0 +1,5 @@
1
+ module Opener
2
+ class OpinionDetector
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,281 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link type="text/css" rel="stylesheet" charset="UTF-8" href="markdown.css"/>
5
+ <title>Opinion Detector Webservice</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opinion Detector Web Service</h1>
9
+
10
+ <h2>Example Usage</h2>
11
+
12
+ <p>
13
+ <pre>opinion-detector-server start</pre>
14
+ <pre>curl -d &#39;input=&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&lt;KAF version=&quot;v1.opener&quot; xml:lang=&quot;en&quot;&gt;&lt;kafHeader&gt;&lt;linguisticProcessors layer=&quot;text&quot;&gt;&lt;lp name=&quot;opennlp-en-tok&quot; timestamp=&quot;2013-06-11T13:41:37Z&quot; version=&quot;1.0&quot;/&gt;&lt;lp name=&quot;opennlp-en-sent&quot; timestamp=&quot;2013-06-11T13:41:37Z&quot; version=&quot;1.0&quot;/&gt;&lt;/linguisticProcessors&gt;&lt;/kafHeader&gt;&lt;text&gt;&lt;wf length=&quot;4&quot; offset=&quot;0&quot; para=&quot;1&quot; sent=&quot;1&quot; wid=&quot;w1&quot;&gt;this&lt;/wf&gt;&lt;wf length=&quot;2&quot; offset=&quot;5&quot; para=&quot;1&quot; sent=&quot;1&quot; wid=&quot;w2&quot;&gt;is&lt;/wf&gt;&lt;wf length=&quot;2&quot; offset=&quot;8&quot; para=&quot;1&quot; sent=&quot;1&quot; wid=&quot;w3&quot;&gt;an&lt;/wf&gt;&lt;wf length=&quot;7&quot; offset=&quot;11&quot; para=&quot;1&quot; sent=&quot;1&quot; wid=&quot;w4&quot;&gt;english&lt;/wf&gt;&lt;wf length=&quot;4&quot; offset=&quot;19&quot; para=&quot;1&quot; sent=&quot;1&quot; wid=&quot;w5&quot;&gt;text&lt;/wf&gt;&lt;/text&gt;&lt;/KAF&gt;&#39; http://localhost:9292 -XPOST</pre>
15
+
16
+ outputs:
17
+
18
+ <pre>
19
+ &lt;?xml version=&#39;1.0&#39; encoding=&#39;UTF-8&#39;?&gt;
20
+ &lt;KAF xml:lang=&quot;en&quot;&gt;
21
+ &lt;kafHeader&gt;
22
+ &lt;linguisticProcessors layer=&quot;text&quot;&gt;
23
+ &lt;lp timestamp=&quot;2013-04-11T09:21:29CEST&quot; version=&quot;1.0&quot; name=&quot;Open-nlp sentence splitter&quot;/&gt;
24
+ &lt;lp timestamp=&quot;2013-04-11T09:21:29CEST&quot; version=&quot;1.0&quot; name=&quot;Open-nlp tokenizer&quot;/&gt;
25
+ &lt;/linguisticProcessors&gt;
26
+ &lt;linguisticProcessor layer=&quot;opinions&quot;&gt;
27
+ &lt;lp timestamp=&quot;2013-06-27T17:56:32EEST&quot; version=&quot;1.0&quot; name=&quot;Crfsuite machine learning opinion miner&quot;/&gt;
28
+ &lt;/linguisticProcessor&gt;
29
+ &lt;linguisticProcessor layer=&quot;terms&quot;&gt;
30
+ &lt;lp timestamp=&quot;2013-04-11T09:23:08CEST&quot; version=&quot;1.0&quot; name=&quot;Basic_polarity_tagger_with_pos&quot;/&gt;
31
+ &lt;/linguisticProcessor&gt;
32
+ &lt;linguisticProcessor layer=&quot;term&quot;&gt;
33
+ &lt;lp timestamp=&quot;2013-04-11T09:23:07CEST&quot; version=&quot;1.0&quot; name=&quot;TreeTagger_from_kaf&quot;/&gt;
34
+ &lt;/linguisticProcessor&gt;
35
+ &lt;/kafHeader&gt;
36
+ &lt;text&gt;
37
+ &lt;wf wid=&quot;w_1&quot; sent=&quot;s_1&quot;&gt;This&lt;/wf&gt;
38
+ &lt;wf wid=&quot;w_2&quot; sent=&quot;s_1&quot;&gt;is&lt;/wf&gt;
39
+ &lt;wf wid=&quot;w_3&quot; sent=&quot;s_1&quot;&gt;a&lt;/wf&gt;
40
+ &lt;wf wid=&quot;w_4&quot; sent=&quot;s_1&quot;&gt;very&lt;/wf&gt;
41
+ &lt;wf wid=&quot;w_5&quot; sent=&quot;s_1&quot;&gt;nice&lt;/wf&gt;
42
+ &lt;wf wid=&quot;w_6&quot; sent=&quot;s_1&quot;&gt;hotel&lt;/wf&gt;
43
+ &lt;wf wid=&quot;w_7&quot; sent=&quot;s_1&quot;&gt;in&lt;/wf&gt;
44
+ &lt;wf wid=&quot;w_8&quot; sent=&quot;s_1&quot;&gt;Amsterdam&lt;/wf&gt;
45
+ &lt;/text&gt;
46
+ &lt;terms&gt;
47
+ &lt;term tid=&quot;t_1&quot; lemma=&quot;this&quot; morphofeat=&quot;DT&quot; type=&quot;close&quot; pos=&quot;D&quot;&gt;
48
+ &lt;span&gt;
49
+ &lt;target id=&quot;w_1&quot;/&gt;
50
+ &lt;/span&gt;
51
+ &lt;/term&gt;
52
+ &lt;term tid=&quot;t_2&quot; lemma=&quot;be&quot; morphofeat=&quot;VBZ&quot; type=&quot;open&quot; pos=&quot;V&quot;&gt;
53
+ &lt;span&gt;
54
+ &lt;target id=&quot;w_2&quot;/&gt;
55
+ &lt;/span&gt;
56
+ &lt;/term&gt;
57
+ &lt;term tid=&quot;t_3&quot; lemma=&quot;a&quot; morphofeat=&quot;DT&quot; type=&quot;close&quot; pos=&quot;D&quot;&gt;
58
+ &lt;span&gt;
59
+ &lt;target id=&quot;w_3&quot;/&gt;
60
+ &lt;/span&gt;
61
+ &lt;/term&gt;
62
+ &lt;term tid=&quot;t_4&quot; lemma=&quot;very&quot; morphofeat=&quot;RB&quot; type=&quot;open&quot; pos=&quot;A&quot;&gt;
63
+ &lt;span&gt;
64
+ &lt;target id=&quot;w_4&quot;/&gt;
65
+ &lt;/span&gt;
66
+ &lt;sentiment sentiment_modifier=&quot;intensifier&quot; resource=&quot;VUA_olery_lexicon_en_lmf&quot;/&gt;
67
+ &lt;/term&gt;
68
+ &lt;term tid=&quot;t_5&quot; lemma=&quot;nice&quot; morphofeat=&quot;JJ&quot; type=&quot;open&quot; pos=&quot;G&quot;&gt;
69
+ &lt;span&gt;
70
+ &lt;target id=&quot;w_5&quot;/&gt;
71
+ &lt;/span&gt;
72
+ &lt;sentiment polarity=&quot;positive&quot; resource=&quot;VUA_olery_lexicon_en_lmf&quot;/&gt;
73
+ &lt;/term&gt;
74
+ &lt;term tid=&quot;t_6&quot; lemma=&quot;hotel&quot; morphofeat=&quot;NN&quot; type=&quot;open&quot; pos=&quot;N&quot;&gt;
75
+ &lt;span&gt;
76
+ &lt;target id=&quot;w_6&quot;/&gt;
77
+ &lt;/span&gt;
78
+ &lt;/term&gt;
79
+ &lt;term tid=&quot;t_7&quot; lemma=&quot;in&quot; morphofeat=&quot;IN&quot; type=&quot;close&quot; pos=&quot;P&quot;&gt;
80
+ &lt;span&gt;
81
+ &lt;target id=&quot;w_7&quot;/&gt;
82
+ &lt;/span&gt;
83
+ &lt;/term&gt;
84
+ &lt;term tid=&quot;t_8&quot; lemma=&quot;Amsterdam&quot; morphofeat=&quot;NP&quot; type=&quot;open&quot; pos=&quot;R&quot;&gt;
85
+ &lt;span&gt;
86
+ &lt;target id=&quot;w_8&quot;/&gt;
87
+ &lt;/span&gt;
88
+ &lt;/term&gt;
89
+ &lt;/terms&gt;
90
+ &lt;entities&gt;
91
+ &lt;entity eid=&quot;e15&quot; type=&quot;location&quot;&gt;
92
+ &lt;references&gt;
93
+ &lt;span&gt;
94
+ &lt;!--Amsterdam --&gt;
95
+ &lt;target id=&quot;t_8&quot;/&gt;
96
+ &lt;/span&gt;
97
+ &lt;/references&gt;
98
+ &lt;/entity&gt;
99
+ &lt;/entities&gt;
100
+ &lt;opinions&gt;
101
+ &lt;opinion oid=&quot;o_1&quot;&gt;
102
+ &lt;opinion_holder&gt;
103
+ &lt;span/&gt;
104
+ &lt;/opinion_holder&gt;
105
+ &lt;opinion_target&gt;
106
+ &lt;span&gt;
107
+ &lt;target id=&quot;t_1&quot;/&gt;
108
+ &lt;/span&gt;
109
+ &lt;/opinion_target&gt;
110
+ &lt;opinion_expression polarity=&quot;positive&quot; strength=&quot;1&quot;&gt;
111
+ &lt;span&gt;
112
+ &lt;target id=&quot;t_4&quot;/&gt;
113
+ &lt;target id=&quot;t_5&quot;/&gt;
114
+ &lt;target id=&quot;t_6&quot;/&gt;
115
+ &lt;target id=&quot;t_7&quot;/&gt;
116
+ &lt;target id=&quot;t_8&quot;/&gt;
117
+ &lt;/span&gt;
118
+ &lt;/opinion_expression&gt;
119
+ &lt;/opinion&gt;
120
+ &lt;/opinions&gt;
121
+ &lt;/KAF&gt;</pre>
122
+ </p>
123
+
124
+ <h2>Try the webservice</h2>
125
+
126
+ <p>* required</p>
127
+ <p>** When entering a value no response will be displayed in the browser.</p>
128
+
129
+ <form action="<%=url("/")%>" method="POST">
130
+ <div>
131
+ <label for="input"/>Type your text here*</label>
132
+ <br/>
133
+
134
+ <textarea name="input" id="text" rows="10" cols="50"/></textarea>
135
+ </div>
136
+ <% 10.times do |t| %>
137
+ <div>
138
+ <label for="callbacks">Callback URL <%=t+1%>(**)</label>
139
+ <br />
140
+
141
+ <input id="callbacks" type="text" name="callbacks[]" />
142
+ </div>
143
+ <% end %>
144
+
145
+ <div>
146
+ <label for="error_callback">Error Callback</label>
147
+ <br />
148
+
149
+ <input id="error_callback" type="text" name="error_callback" />
150
+ </div>
151
+ <input type="submit" value="Submit" />
152
+ </form>
153
+
154
+ <h2>Actions</h2>
155
+
156
+ <p>
157
+ <dl>
158
+ <dt>POST /</dt>
159
+ <dd>Detect opinion for input text. See arguments listing for more options.</dd>
160
+ <dt>GET /</dt>
161
+ <dd>Show this page</dd>
162
+ </dl>
163
+ </p>
164
+
165
+ <h2>Arguments</h2>
166
+
167
+ <p> The webservice takes the following arguments: </p>
168
+ <p>* required</p>
169
+
170
+ <dl>
171
+ <dt>text*</dt>
172
+ <dd>The input text in KAF format. Sample KAF input:</dd>
173
+ <pre>
174
+ &lt;?xml version=&#39;1.0&#39; encoding=&#39;UTF-8&#39;?&gt;
175
+ &lt;KAF xml:lang=&quot;en&quot;&gt;
176
+ &lt;kafHeader&gt;
177
+ &lt;linguisticProcessors layer=&quot;text&quot;&gt;
178
+ &lt;lp timestamp=&quot;2013-04-11T09:21:29CEST&quot; version=&quot;1.0&quot; name=&quot;Open-nlp sentence splitter&quot;/&gt;
179
+ &lt;lp timestamp=&quot;2013-04-11T09:21:29CEST&quot; version=&quot;1.0&quot; name=&quot;Open-nlp tokenizer&quot;/&gt;
180
+ &lt;/linguisticProcessors&gt;
181
+ &lt;linguisticProcessor layer=&quot;terms&quot;&gt;
182
+ &lt;lp timestamp=&quot;2013-04-11T09:23:08CEST&quot; version=&quot;1.0&quot; name=&quot;Basic_polarity_tagger_with_pos&quot;/&gt;
183
+ &lt;/linguisticProcessor&gt;
184
+ &lt;linguisticProcessor layer=&quot;term&quot;&gt;
185
+ &lt;lp timestamp=&quot;2013-04-11T09:23:07CEST&quot; version=&quot;1.0&quot; name=&quot;TreeTagger_from_kaf&quot;/&gt;
186
+ &lt;/linguisticProcessor&gt;
187
+ &lt;/kafHeader&gt;
188
+ &lt;text&gt;
189
+ &lt;wf wid=&quot;w_1&quot; sent=&quot;s_1&quot;&gt;This&lt;/wf&gt;
190
+ &lt;wf wid=&quot;w_2&quot; sent=&quot;s_1&quot;&gt;is&lt;/wf&gt;
191
+ &lt;wf wid=&quot;w_3&quot; sent=&quot;s_1&quot;&gt;a&lt;/wf&gt;
192
+ &lt;wf wid=&quot;w_4&quot; sent=&quot;s_1&quot;&gt;very&lt;/wf&gt;
193
+ &lt;wf wid=&quot;w_5&quot; sent=&quot;s_1&quot;&gt;nice&lt;/wf&gt;
194
+ &lt;wf wid=&quot;w_6&quot; sent=&quot;s_1&quot;&gt;hotel&lt;/wf&gt;
195
+ &lt;wf wid=&quot;w_7&quot; sent=&quot;s_1&quot;&gt;in&lt;/wf&gt;
196
+ &lt;wf wid=&quot;w_8&quot; sent=&quot;s_1&quot;&gt;Amsterdam&lt;/wf&gt;
197
+ &lt;/text&gt;
198
+ &lt;terms&gt;
199
+ &lt;term tid=&quot;t_1&quot; lemma=&quot;this&quot; morphofeat=&quot;DT&quot; type=&quot;close&quot; pos=&quot;D&quot;&gt;
200
+ &lt;span&gt;
201
+ &lt;target id=&quot;w_1&quot;/&gt;
202
+ &lt;/span&gt;
203
+ &lt;/term&gt;
204
+ &lt;term tid=&quot;t_2&quot; lemma=&quot;be&quot; morphofeat=&quot;VBZ&quot; type=&quot;open&quot; pos=&quot;V&quot;&gt;
205
+ &lt;span&gt;
206
+ &lt;target id=&quot;w_2&quot;/&gt;
207
+ &lt;/span&gt;
208
+ &lt;/term&gt;
209
+ &lt;term tid=&quot;t_3&quot; lemma=&quot;a&quot; morphofeat=&quot;DT&quot; type=&quot;close&quot; pos=&quot;D&quot;&gt;
210
+ &lt;span&gt;
211
+ &lt;target id=&quot;w_3&quot;/&gt;
212
+ &lt;/span&gt;
213
+ &lt;/term&gt;
214
+ &lt;term tid=&quot;t_4&quot; lemma=&quot;very&quot; morphofeat=&quot;RB&quot; type=&quot;open&quot; pos=&quot;A&quot;&gt;
215
+ &lt;span&gt;
216
+ &lt;target id=&quot;w_4&quot;/&gt;
217
+ &lt;/span&gt;
218
+ &lt;sentiment sentiment_modifier=&quot;intensifier&quot; resource=&quot;VUA_olery_lexicon_en_lmf&quot;/&gt;
219
+ &lt;/term&gt;
220
+ &lt;term tid=&quot;t_5&quot; lemma=&quot;nice&quot; morphofeat=&quot;JJ&quot; type=&quot;open&quot; pos=&quot;G&quot;&gt;
221
+ &lt;span&gt;
222
+ &lt;target id=&quot;w_5&quot;/&gt;
223
+ &lt;/span&gt;
224
+ &lt;sentiment polarity=&quot;positive&quot; resource=&quot;VUA_olery_lexicon_en_lmf&quot;/&gt;
225
+ &lt;/term&gt;
226
+ &lt;term tid=&quot;t_6&quot; lemma=&quot;hotel&quot; morphofeat=&quot;NN&quot; type=&quot;open&quot; pos=&quot;N&quot;&gt;
227
+ &lt;span&gt;
228
+ &lt;target id=&quot;w_6&quot;/&gt;
229
+ &lt;/span&gt;
230
+ &lt;/term&gt;
231
+ &lt;term tid=&quot;t_7&quot; lemma=&quot;in&quot; morphofeat=&quot;IN&quot; type=&quot;close&quot; pos=&quot;P&quot;&gt;
232
+ &lt;span&gt;
233
+ &lt;target id=&quot;w_7&quot;/&gt;
234
+ &lt;/span&gt;
235
+ &lt;/term&gt;
236
+ &lt;term tid=&quot;t_8&quot; lemma=&quot;Amsterdam&quot; morphofeat=&quot;NP&quot; type=&quot;open&quot; pos=&quot;R&quot;&gt;
237
+ &lt;span&gt;
238
+ &lt;target id=&quot;w_8&quot;/&gt;
239
+ &lt;/span&gt;
240
+ &lt;/term&gt;
241
+ &lt;/terms&gt;
242
+ &lt;entities&gt;
243
+ &lt;entity eid=&quot;e15&quot; type=&quot;location&quot;&gt;
244
+ &lt;references&gt;
245
+ &lt;span&gt;
246
+ &lt;!--Amsterdam --&gt;
247
+ &lt;target id=&quot;t_8&quot; /&gt;
248
+ &lt;/span&gt;
249
+ &lt;/references&gt;
250
+ &lt;/entity&gt;
251
+ &lt;/entities&gt;
252
+ &lt;/KAF&gt;</pre>
253
+
254
+ <dt>callbacks</dt>
255
+ <dd>
256
+ You can provide a list of callback urls. If you provide callback urls
257
+ the Opinion detector will run as a background job and a callback
258
+ with the results will be performed (POST) to the first url in the callback
259
+ list. The other urls in callback list will be provided in the "callbacks"
260
+ argument.<br/><br/>
261
+ Using callback you can chain together several OpeNER webservices in
262
+ one call. The first, will call the second, which will call the third, etc.
263
+ See for more information the <a href="http://opener-project.github.io">
264
+ webservice documentation online</a>.
265
+ </dd>
266
+ <dt>error_callback</dt>
267
+ <dd>URL to notify if errors occur in the background process. The error
268
+ callback will do a POST with the error message in the 'error' field.</dd>
269
+ </dt>
270
+
271
+
272
+
273
+ </dl>
274
+
275
+
276
+ <p>
277
+
278
+ </p>
279
+
280
+ </body>
281
+ </html>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link type="text/css" rel="stylesheet" charset="UTF-8" href="markdown.css"/>
5
+ <title>Language Detector Webservice</title>
6
+ </head>
7
+ <body>
8
+ <h1>Output URL</h1>
9
+ <p>
10
+ When ready, you can view the result
11
+ <a href=<%= output_url %>>here</a>
12
+ </p>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,33 @@
1
+ require File.expand_path('../lib/opener/opinion_detector/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'opener-opinion-detector'
5
+ gem.version = Opener::OpinionDetector::VERSION
6
+ gem.authors = ['development@olery.com']
7
+ gem.summary = 'Gem that wraps up the different existing opinion detectors'
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
+ 'lib/**/*',
15
+ 'config.ru',
16
+ '*.gemspec',
17
+ 'README.md'
18
+ ]).select { |file| File.file?(file) }
19
+
20
+ gem.executables = Dir.glob('bin/*').map { |file| File.basename(file) }
21
+
22
+ gem.add_dependency 'opener-opinion-detector-base'
23
+ gem.add_dependency 'nokogiri'
24
+ gem.add_dependency 'sinatra', '~>1.4.2'
25
+ gem.add_dependency 'httpclient'
26
+ gem.add_dependency 'opener-webservice'
27
+
28
+ gem.add_development_dependency 'rspec'
29
+ gem.add_development_dependency 'cucumber'
30
+ gem.add_development_dependency 'pry'
31
+ gem.add_development_dependency 'rake'
32
+ end
33
+
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-opinion-detector
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-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opener-opinion-detector-base
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: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sinatra
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.4.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.4.2
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: opener-webservice
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: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
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: cucumber
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
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: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
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: rake
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
+ description: Gem that wraps up the different existing opinion detectors
140
+ email:
141
+ executables:
142
+ - opinion-detector-server
143
+ - opinion-detector
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - README.md
148
+ - bin/opinion-detector
149
+ - bin/opinion-detector-server
150
+ - config.ru
151
+ - lib/opener/opinion_detector.rb
152
+ - lib/opener/opinion_detector/cli.rb
153
+ - lib/opener/opinion_detector/public/markdown.css
154
+ - lib/opener/opinion_detector/server.rb
155
+ - lib/opener/opinion_detector/version.rb
156
+ - lib/opener/opinion_detector/views/index.erb
157
+ - lib/opener/opinion_detector/views/result.erb
158
+ - opener-opinion-detector.gemspec
159
+ homepage: http://opener-project.github.com/
160
+ licenses: []
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: 1.9.2
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.2.2
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: Gem that wraps up the different existing opinion detectors
182
+ test_files: []
183
+ has_rdoc: yard