opener-opinion-detector 1.0.0

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