Commit 635b2a48ed59a97b6a49214f0f5ea46bac377545
1 parent
42ed698c
Exists in
master
Using new twitter stream lib
Showing
36 changed files
with
76 additions
and
2126 deletions
Show diff stats
lib/community_hub_plugin/hub.rb
1 | -require File.dirname(__FILE__) + '/../../tweeter_stream/lib/twurl' | |
1 | +require File.dirname(__FILE__) + '/../../twitter/stream.rb' | |
2 | 2 | require File.dirname(__FILE__) + '/../../facebook_stream/lib_facebook_stream' |
3 | 3 | |
4 | 4 | class CommunityHubPlugin::Hub < Folder |
5 | 5 | |
6 | 6 | settings_items :proxy_url, :type => :string, :default => 'http://161.148.1.167:3128' # Remember to use add the port, in case needed. |
7 | 7 | settings_items :twitter_enabled, :type => :boolean, :default => false |
8 | - settings_items :hashtags_twitter, :type => :string, :default => "participa.br,participabr,arenanetmundial,netmundial" | |
9 | - settings_items :twitter_token_file, :type => :string, :default => "twurlrc_mariajoseopinto" | |
8 | + settings_items :twitter_hashtags, :type => :string, :default => "participa.br,participabr,arenanetmundial,netmundial" | |
9 | + settings_items :twitter_consumer_key, :type => :string, :default => "" | |
10 | + settings_items :twitter_consumer_secret, :type => :string, :default => "" | |
11 | + settings_items :twitter_access_token, :type => :string, :default => "" | |
12 | + settings_items :twitter_access_token_secret, :type => :string, :default => "" | |
10 | 13 | settings_items :facebook_enabled, :type => :boolean, :default => false |
11 | 14 | settings_items :facebook_page_id, :type => :string, :default => "participabr" |
12 | 15 | settings_items :facebook_pooling_time, :type => :integer, :default => 5 # Time in seconds | ... | ... |
lib/community_hub_plugin/listener.rb
... | ... | @@ -5,8 +5,7 @@ class CommunityHubPlugin::Listener |
5 | 5 | class << self |
6 | 6 | |
7 | 7 | def twitter_service(hub) |
8 | - hub.twitter_token_file ||= 'twurlrc' | |
9 | - Twurl::Stream.run(hub, nil, hub.hashtags_twitter, File.dirname(__FILE__) + '/../../tweeter_stream/config/' + hub.twitter_token_file, hub.proxy_url) | |
8 | + listen_twitter_stream(hub, nil) | |
10 | 9 | end |
11 | 10 | |
12 | 11 | def facebook_service(hub) | ... | ... |
tweeter_stream/.gemtest
tweeter_stream/.gitignore
... | ... | @@ -1,39 +0,0 @@ |
1 | -*.gem | |
2 | -*.rbc | |
3 | -*.sw[a-p] | |
4 | -*.tmproj | |
5 | -*.tmproject | |
6 | -*.un~ | |
7 | -*~ | |
8 | -.Spotlight-V100 | |
9 | -.Trashes | |
10 | -._* | |
11 | -.bundle | |
12 | -.config | |
13 | -.directory | |
14 | -.elc | |
15 | -.emacs.desktop | |
16 | -.emacs.desktop.lock | |
17 | -.redcar | |
18 | -.yardoc | |
19 | -Desktop.ini | |
20 | -Gemfile.lock | |
21 | -Icon? | |
22 | -InstalledFiles | |
23 | -Session.vim | |
24 | -\#*\# | |
25 | -_yardoc | |
26 | -auto-save-list | |
27 | -coverage | |
28 | -doc | |
29 | -lib/bundler/man | |
30 | -pkg | |
31 | -pkg/* | |
32 | -rdoc | |
33 | -spec/reports | |
34 | -test/tmp | |
35 | -test/version_tmp | |
36 | -tmp | |
37 | -tmtags | |
38 | -tramp | |
39 | -/nbproject/private/ | |
40 | 0 | \ No newline at end of file |
tweeter_stream/.travis.yml
tweeter_stream/COPYING
... | ... | @@ -1,18 +0,0 @@ |
1 | -# Copyright (c) 2009 Marcel Molina <marcel@twitter.com>, Twitter, Inc. | |
2 | -# | |
3 | -# Permission is hereby granted, free of charge, to any person obtaining a copy of | |
4 | -# this software and associated documentation files (the "Software"), to deal in the | |
5 | -# Software without restriction, including without limitation the rights to use, | |
6 | -# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the | |
7 | -# Software, and to permit persons to whom the Software is furnished to do so, | |
8 | -# subject to the following conditions: | |
9 | -# | |
10 | -# The above copyright notice and this permission notice shall be included in all | |
11 | -# copies or substantial portions of the Software. | |
12 | -# | |
13 | -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
15 | -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
16 | -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |
17 | -# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
18 | -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
tweeter_stream/Gemfile
... | ... | @@ -1,15 +0,0 @@ |
1 | -source 'https://rubygems.org' | |
2 | - | |
3 | -gem 'jruby-openssl', :platforms => :jruby | |
4 | -gem 'rake' | |
5 | -gem 'json' | |
6 | - | |
7 | -group :test do | |
8 | - gem 'coveralls', :require => false | |
9 | - gem 'mime-types', '~> 1.25', :platforms => :ruby_18 | |
10 | - gem 'minitest', '>= 5' | |
11 | - gem 'rr', '>= 1.1' | |
12 | - gem 'simplecov', :require => false | |
13 | -end | |
14 | - | |
15 | -gemspec |
tweeter_stream/Gemfile.lock
... | ... | @@ -1,49 +0,0 @@ |
1 | -PATH | |
2 | - remote: . | |
3 | - specs: | |
4 | - twurl (0.9.2) | |
5 | - oauth (~> 0.4) | |
6 | - | |
7 | -GEM | |
8 | - remote: https://rubygems.org/ | |
9 | - specs: | |
10 | - coveralls (0.7.0) | |
11 | - multi_json (~> 1.3) | |
12 | - rest-client | |
13 | - simplecov (>= 0.7) | |
14 | - term-ansicolor | |
15 | - thor | |
16 | - docile (1.1.3) | |
17 | - json (1.8.1) | |
18 | - mime-types (1.25.1) | |
19 | - minitest (5.3.2) | |
20 | - multi_json (1.9.2) | |
21 | - oauth (0.4.7) | |
22 | - rake (10.2.2) | |
23 | - rest-client (1.6.7) | |
24 | - mime-types (>= 1.16) | |
25 | - rr (1.1.2) | |
26 | - simplecov (0.8.2) | |
27 | - docile (~> 1.1.0) | |
28 | - multi_json | |
29 | - simplecov-html (~> 0.8.0) | |
30 | - simplecov-html (0.8.0) | |
31 | - term-ansicolor (1.3.0) | |
32 | - tins (~> 1.0) | |
33 | - thor (0.19.1) | |
34 | - tins (1.1.0) | |
35 | - | |
36 | -PLATFORMS | |
37 | - ruby | |
38 | - | |
39 | -DEPENDENCIES | |
40 | - bundler (~> 1.0) | |
41 | - coveralls | |
42 | - jruby-openssl | |
43 | - json | |
44 | - mime-types (~> 1.25) | |
45 | - minitest (>= 5) | |
46 | - rake | |
47 | - rr (>= 1.1) | |
48 | - simplecov | |
49 | - twurl! |
tweeter_stream/INSTALL
... | ... | @@ -1,18 +0,0 @@ |
1 | -+-----------------------+ | |
2 | -| Install with RubyGems | | |
3 | -+-----------------------+ | |
4 | - | |
5 | - sudo gem i twurl --source http://rubygems.org | |
6 | - | |
7 | -+---------------------+ | |
8 | -| Install from source | | |
9 | -+---------------------+ | |
10 | - | |
11 | - rake dist:gem | |
12 | - sudo gem i pkg/twurl*gem | |
13 | - | |
14 | -+--------------+ | |
15 | -| Dependencies | | |
16 | -+--------------+ | |
17 | - | |
18 | - sudo gem i oauth |
tweeter_stream/README
... | ... | @@ -1,119 +0,0 @@ |
1 | -+-------+ | |
2 | -| Twurl | | |
3 | -+-------+ | |
4 | - | |
5 | -Twurl is like curl, but tailored specifically for the Twitter API. | |
6 | -It knows how to grant an access token to a client application for | |
7 | -a specified user and then sign all requests with that access token. | |
8 | - | |
9 | -It also provides other development and debugging conveniences such | |
10 | -as defining aliases for common requests, as well as support for | |
11 | -multiple access tokens to easily switch between different client | |
12 | -applications and Twitter accounts. | |
13 | - | |
14 | -+-----------------+ | |
15 | -| Getting Started | | |
16 | -+-----------------+ | |
17 | - | |
18 | -The first thing you have to do is register an OAuth application | |
19 | -to get a consumer key and secret. | |
20 | - | |
21 | - http://dev.twitter.com/apps/new | |
22 | - | |
23 | -When you have your consumer key and its secret you authorize | |
24 | -your Twitter account to make API requests with your consumer key | |
25 | -and secret. | |
26 | - | |
27 | - % twurl authorize --consumer-key key \ | |
28 | - --consumer-secret secret | |
29 | - | |
30 | -This will return an URL that you should open up in your browser. | |
31 | -Authenticate to Twitter, and then enter the returned PIN back into | |
32 | -the terminal. Assuming all that works well, you will beauthorized | |
33 | -to make requests with the API. Twurl will tell you as much. | |
34 | - | |
35 | -If your consumer application has xAuth enabled, then you can use | |
36 | -a variant of the above | |
37 | - | |
38 | - % twurl authorize -u username -p password \ | |
39 | - --consumer-key key \ | |
40 | - --consumer-secret secret | |
41 | - | |
42 | -And, again assuming your username, password, key and secret is | |
43 | -correct, will authorize you in one step. | |
44 | - | |
45 | -+-----------------+ | |
46 | -| Making Requests | | |
47 | -+-----------------+ | |
48 | - | |
49 | -The simplest request just requires that you specify the path you | |
50 | -want to request. | |
51 | - | |
52 | - % twurl /1.1/statuses/home_timeline.json | |
53 | - | |
54 | -Similar to curl, a GET request is performed by default. | |
55 | - | |
56 | -You can implicitly perform a POST request by passing the -d option, | |
57 | -which specifies POST parameters. | |
58 | - | |
59 | - % twurl -d 'status=Testing twurl' /1.1/statuses/update.json | |
60 | - | |
61 | -You can explicitly specify what request method to perform with | |
62 | -the -X (or --request-method) option. | |
63 | - | |
64 | - % twurl -X POST /1.1/statuses/destroy/1234567890.json | |
65 | - | |
66 | -+------------------+ | |
67 | -| Creating aliases | | |
68 | -+------------------+ | |
69 | - | |
70 | - % twurl alias h /1.1/statuses/home_timeline.json | |
71 | - | |
72 | -You can then use "h" in place of the full path. | |
73 | - | |
74 | - % twurl h | |
75 | - | |
76 | -Paths that require additional options such as request parameters for example can | |
77 | -be used with aliases the same as with full explicit paths, just as you might | |
78 | -expect. | |
79 | - | |
80 | - % twurl alias tweet /1.1/statuses/update.json | |
81 | - % twurl tweet -d "status=Aliases in twurl are convenient" | |
82 | - | |
83 | -+-------------------------------+ | |
84 | -| Changing your default profile | | |
85 | -+-------------------------------+ | |
86 | - | |
87 | -The first time you authorize a client application to make requests on behalf of your account, twurl stores your access token information in its .twurlrc file. Subsequent requests will use this profile as the default profile. You can use the 'accounts' subcommand to see what client applications have been authorized for what user names: | |
88 | - | |
89 | - % twurl accounts | |
90 | - noradio | |
91 | - HQsAGcBm5MQT4n6j7qVJw | |
92 | - hhC7Koy2zRsTZvQh1hVlSA (default) | |
93 | - testiverse | |
94 | - guT9RsJbNQgVe6AwoY9BA | |
95 | - | |
96 | -Notice that one of those consumer keys is marked as the default. To change the default use the 'set' subcommand, passing then either just the username, if it's unambiguous, or the username and consumer key pair if it isn't unambiguous: | |
97 | - | |
98 | - % twurl set default testiverse | |
99 | - % twurl accounts | |
100 | - noradio | |
101 | - HQsAGcBm5MQT4n6j7qVJw | |
102 | - hhC7Koy2zRsTZvQh1hVlSA | |
103 | - testiverse | |
104 | - guT9RsJbNQgVe6AwoY9BA (default) | |
105 | - | |
106 | - % twurl set default noradio HQsAGcBm5MQT4n6j7qVJw | |
107 | - % twurl accounts | |
108 | - noradio | |
109 | - HQsAGcBm5MQT4n6j7qVJw (default) | |
110 | - hhC7Koy2zRsTZvQh1hVlSA | |
111 | - testiverse | |
112 | - guT9RsJbNQgVe6AwoY9BA | |
113 | - | |
114 | -+--------------+ | |
115 | -| Contributors | | |
116 | -+--------------+ | |
117 | - | |
118 | -Marcel Molina <marcel@twitter.com> / @noradio | |
119 | -Erik Michaels-Ober / @sferik |
tweeter_stream/Rakefile
tweeter_stream/config/twurlrc
... | ... | @@ -1,13 +0,0 @@ |
1 | ---- | |
2 | -profiles: | |
3 | - evandro: | |
4 | - QkiWnQvutoV63ty0Afg: | |
5 | - token: 8601482-U3qvrGvZmKgm5HnTCskdYU2bAoMPzNpXcLtdVzhKQx | |
6 | - consumer_key: QkiWnQvutoV63ty0Afg | |
7 | - username: evandro | |
8 | - consumer_secret: kCLHpOG2p3073mYsxrTYyvwkOulYbtIcxpQkthJU | |
9 | - secret: eCzPWQOcUdcADRmFjqQTIXPbmieOxMpXDj7RUS6mVrPru | |
10 | -configuration: | |
11 | - default_profile: | |
12 | - - evandro | |
13 | - - QkiWnQvutoV63ty0Afg |
tweeter_stream/config/twurlrc_mariajoseopinto
... | ... | @@ -1,13 +0,0 @@ |
1 | ---- | |
2 | -profiles: | |
3 | - MariaJoseOPinto: | |
4 | - c7dZNecaM2gF5Jx0bRHxocLmv: | |
5 | - token: 2450103578-CXZK39hmoZhLuGecQou9jympNl9uXG0vB9Xmuks | |
6 | - consumer_key: c7dZNecaM2gF5Jx0bRHxocLmv | |
7 | - username: MariaJoseOPinto | |
8 | - consumer_secret: uDIuSmCJaCgDMk1uuumaibzBQk4imlw4g9IdpVtC7Xo3Jj6mog | |
9 | - secret: HE7gKVgEl11PkGlKUtUwWj9XYwOFOf4DprhlZxOZ25GXl | |
10 | -configuration: | |
11 | - default_profile: | |
12 | - - MariaJoseOPinto | |
13 | - - c7dZNecaM2gF5Jx0bRHxocLmv |
tweeter_stream/lib/cli.rb
... | ... | @@ -1,368 +0,0 @@ |
1 | -module Twurl | |
2 | - class CLI | |
3 | - SUPPORTED_COMMANDS = %w(authorize accounts alias set) | |
4 | - DEFAULT_COMMAND = 'request' | |
5 | - PATH_PATTERN = /^\/\w+/ | |
6 | - PROTOCOL_PATTERN = /^\w+:\/\// | |
7 | - README = File.dirname(__FILE__) + '/../../README' | |
8 | - @output ||= STDOUT | |
9 | - class NoPathFound < Exception | |
10 | - end | |
11 | - | |
12 | - class << self | |
13 | - attr_accessor :output | |
14 | - | |
15 | - def run(args) | |
16 | - begin | |
17 | - options = parse_options(args) | |
18 | - rescue NoPathFound => e | |
19 | - exit | |
20 | - end | |
21 | - dispatch(options) | |
22 | - end | |
23 | - | |
24 | - def dispatch(options) | |
25 | - client = OAuthClient.load_from_options(options) | |
26 | - controller = case options.command | |
27 | - when 'authorize' | |
28 | - AuthorizationController | |
29 | - when 'accounts' | |
30 | - AccountInformationController | |
31 | - when 'alias' | |
32 | - AliasesController | |
33 | - when 'set' | |
34 | - ConfigurationController | |
35 | - when 'request' | |
36 | - RequestController | |
37 | - end | |
38 | - controller.dispatch(client, options) | |
39 | - rescue Twurl::Exception => exception | |
40 | - abort(exception.message) | |
41 | - end | |
42 | - | |
43 | - def parse_options(args) | |
44 | - arguments = args.dup | |
45 | - | |
46 | - Twurl.options = Options.new | |
47 | - Twurl.options.trace = false | |
48 | - Twurl.options.data = {} | |
49 | - Twurl.options.headers = {} | |
50 | - Twurl.options.upload = {} | |
51 | - Twurl.options.upload['file'] = [] | |
52 | - | |
53 | - option_parser = OptionParser.new do |o| | |
54 | - o.extend AvailableOptions | |
55 | - | |
56 | - o.banner = <<-BANNER | |
57 | -Usage: twurl authorize --consumer-key key --consumer-secret secret | |
58 | - twurl [options] /1.1/statuses/home_timeline.json | |
59 | - | |
60 | -Supported Commands: #{SUPPORTED_COMMANDS.sort.join(', ')} | |
61 | - BANNER | |
62 | - | |
63 | - o.section "Getting started:" do | |
64 | - tutorial | |
65 | - end | |
66 | - | |
67 | - o.section "Authorization options:" do | |
68 | - username | |
69 | - password | |
70 | - consumer_key | |
71 | - consumer_secret | |
72 | - access_token | |
73 | - token_secret | |
74 | - end | |
75 | - | |
76 | - o.section "Common options:" do | |
77 | - trace | |
78 | - data | |
79 | - headers | |
80 | - host | |
81 | - quiet | |
82 | - disable_ssl | |
83 | - request_method | |
84 | - help | |
85 | - version | |
86 | - proxy | |
87 | - file | |
88 | - filefield | |
89 | - base64 | |
90 | - read_timeout | |
91 | - end | |
92 | - end | |
93 | - | |
94 | - arguments = option_parser.parse!(args) | |
95 | - Twurl.options.command = extract_command!(arguments) | |
96 | - Twurl.options.path = extract_path!(arguments) | |
97 | - | |
98 | - if Twurl.options.command == DEFAULT_COMMAND and Twurl.options.path.nil? | |
99 | - CLI.puts option_parser | |
100 | - raise NoPathFound, "No path found" | |
101 | - end | |
102 | - | |
103 | - Twurl.options.subcommands = arguments | |
104 | - Twurl.options | |
105 | - end | |
106 | - | |
107 | - def output | |
108 | - if Twurl.options && Twurl.options.output | |
109 | - Twurl.options.output | |
110 | - else | |
111 | - @output | |
112 | - end | |
113 | - end | |
114 | - | |
115 | - def print(*args, &block) | |
116 | - output.print(*args, &block) | |
117 | - output.flush if output.respond_to?(:flush) | |
118 | - end | |
119 | - | |
120 | - def puts(*args, &block) | |
121 | - output.puts(*args, &block) | |
122 | - output.flush if output.respond_to?(:flush) | |
123 | - end | |
124 | - | |
125 | - def prompt_for(label) | |
126 | - system "stty -echo" | |
127 | - CLI.print "#{label}: " | |
128 | - result = STDIN.gets.chomp | |
129 | - CLI.puts | |
130 | - result | |
131 | - rescue Interrupt | |
132 | - exit | |
133 | - ensure | |
134 | - system "stty echo" | |
135 | - end | |
136 | - | |
137 | - private | |
138 | - def extract_command!(arguments) | |
139 | - if SUPPORTED_COMMANDS.include?(arguments.first) | |
140 | - arguments.shift | |
141 | - else | |
142 | - DEFAULT_COMMAND | |
143 | - end | |
144 | - end | |
145 | - | |
146 | - def extract_path!(arguments) | |
147 | - path = nil | |
148 | - arguments.each_with_index do |argument, index| | |
149 | - if argument[PATH_PATTERN] | |
150 | - path_with_params = arguments.slice!(index) | |
151 | - path, params = path_with_params.split("?", 2) | |
152 | - if params | |
153 | - path += "?" + escape_params(params) | |
154 | - end | |
155 | - break | |
156 | - end | |
157 | - end | |
158 | - path | |
159 | - end | |
160 | - | |
161 | - def escape_params(params) | |
162 | - split_params = params.split("&").map do |param| | |
163 | - key, value = param.split('=', 2) | |
164 | - CGI::escape(key) + '=' + CGI::escape(value) | |
165 | - end | |
166 | - split_params.join("&") | |
167 | - end | |
168 | - end | |
169 | - | |
170 | - module AvailableOptions | |
171 | - def options | |
172 | - Twurl.options | |
173 | - end | |
174 | - | |
175 | - def section(heading, &block) | |
176 | - separator "" | |
177 | - separator heading | |
178 | - | |
179 | - instance_eval(&block) | |
180 | - end | |
181 | - | |
182 | - def tutorial | |
183 | - on('-T', '--tutorial', "Narrative overview of how to get started using Twurl") do | |
184 | - CLI.puts IO.read(README) | |
185 | - exit | |
186 | - end | |
187 | - end | |
188 | - | |
189 | - def consumer_key | |
190 | - on('-c', '--consumer-key [key]', "Your consumer key (required)") do |key| | |
191 | - options.consumer_key = key ? key : CLI.prompt_for('Consumer key') | |
192 | - end | |
193 | - end | |
194 | - | |
195 | - def consumer_secret | |
196 | - on('-s', '--consumer-secret [secret]', "Your consumer secret (required)") do |secret| | |
197 | - options.consumer_secret = secret ? secret : CLI.prompt_for('Consumer secret') | |
198 | - end | |
199 | - end | |
200 | - | |
201 | - def access_token | |
202 | - on('-a', '--access-token [token]', 'Your access token') do |token| | |
203 | - options.access_token = token | |
204 | - end | |
205 | - end | |
206 | - | |
207 | - def token_secret | |
208 | - on('-S', '--token-secret [secret]', "Your token secret") do |secret| | |
209 | - options.token_secret = secret | |
210 | - end | |
211 | - end | |
212 | - | |
213 | - def username | |
214 | - on('-u', '--username [username]', 'Username of account to authorize (required)') do |username| | |
215 | - options.username = username | |
216 | - end | |
217 | - end | |
218 | - | |
219 | - def password | |
220 | - on('-p', '--password [password]', 'Password of account to authorize (required)') do |password| | |
221 | - options.password = password ? password : CLI.prompt_for('Password') | |
222 | - end | |
223 | - end | |
224 | - | |
225 | - def trace | |
226 | - on('-t', '--[no-]trace', 'Trace request/response traffic (default: --no-trace)') do |trace| | |
227 | - options.trace = trace | |
228 | - end | |
229 | - end | |
230 | - | |
231 | - def data | |
232 | - on('-d', '--data [data]', 'Sends the specified data in a POST request to the HTTP server.') do |data| | |
233 | - data.split('&').each do |pair| | |
234 | - key, value = pair.split('=', 2) | |
235 | - options.data[key] = value | |
236 | - end | |
237 | - end | |
238 | - end | |
239 | - | |
240 | - def headers | |
241 | - on('-A', '--header [header]', 'Adds the specified header to the request to the HTTP server.') do |header| | |
242 | - key, value = header.split(': ') | |
243 | - options.headers[key] = value | |
244 | - end | |
245 | - end | |
246 | - | |
247 | - def host | |
248 | - on('-H', '--host [host]', 'Specify host to make requests to (default: api.twitter.com)') do |host| | |
249 | - if host[PROTOCOL_PATTERN] | |
250 | - protocol, protocolless_host = host.split(PROTOCOL_PATTERN, 2) | |
251 | - options.host = protocolless_host | |
252 | - else | |
253 | - options.host = host | |
254 | - end | |
255 | - end | |
256 | - end | |
257 | - | |
258 | - def quiet | |
259 | - on('-q', '--quiet', 'Suppress all output (default: output is printed to STDOUT)') do |quiet| | |
260 | - options.output = StringIO.new | |
261 | - end | |
262 | - end | |
263 | - | |
264 | - def disable_ssl | |
265 | - on('-U', '--no-ssl', 'Disable SSL (default: SSL is enabled)') do |use_ssl| | |
266 | - options.protocol = 'http' | |
267 | - end | |
268 | - end | |
269 | - | |
270 | - def request_method | |
271 | - on('-X', '--request-method [method]', 'Request method (default: GET)') do |request_method| | |
272 | - options.request_method = request_method.downcase | |
273 | - end | |
274 | - end | |
275 | - | |
276 | - def help | |
277 | - on_tail("-h", "--help", "Show this message") do | |
278 | - CLI.puts self | |
279 | - exit | |
280 | - end | |
281 | - end | |
282 | - | |
283 | - def version | |
284 | - on_tail("-v", "--version", "Show version") do | |
285 | - CLI.puts Version | |
286 | - exit | |
287 | - end | |
288 | - end | |
289 | - | |
290 | - def proxy | |
291 | - on('-P', '--proxy [proxy]', 'Specify HTTP proxy to forward requests to (default: No proxy)') do |proxy| | |
292 | - options.proxy = proxy | |
293 | - end | |
294 | - end | |
295 | - | |
296 | - def file | |
297 | - on('-f', '--file [path_to_file]', 'Specify the path to the file to upload') do |file| | |
298 | - if File.file?(file) | |
299 | - options.upload['file'] << file | |
300 | - else | |
301 | - CLI.puts "ERROR: File not found" | |
302 | - exit | |
303 | - end | |
304 | - end | |
305 | - end | |
306 | - | |
307 | - def filefield | |
308 | - on('-F', '--file-field [field_name]', 'Specify the POST parameter name for the file upload data (default: media)') do |filefield| | |
309 | - options.upload['filefield'] = filefield | |
310 | - end | |
311 | - end | |
312 | - | |
313 | - def base64 | |
314 | - on('-b', '--base64', 'Encode the uploaded file as base64 (default: false)') do |base64| | |
315 | - options.upload['base64'] = base64 | |
316 | - end | |
317 | - end | |
318 | - | |
319 | - def read_timeout | |
320 | - on('-y', '--read-timeout [read_timeout]', 'Http Read Timeout (default: Net::HTTP read_timeout)') do |read_timeout| | |
321 | - options.read_timeout = read_timeout.to_i | |
322 | - end | |
323 | - end | |
324 | - end | |
325 | - end | |
326 | - | |
327 | - | |
328 | - class Options < OpenStruct | |
329 | - DEFAULT_REQUEST_METHOD = 'get' | |
330 | - DEFAULT_HOST = 'api.twitter.com' | |
331 | - DEFAULT_PROTOCOL = 'https' | |
332 | - | |
333 | - def oauth_client_options | |
334 | - OAuthClient::OAUTH_CLIENT_OPTIONS.inject({}) do |options, option| | |
335 | - options[option] = send(option) | |
336 | - options | |
337 | - end | |
338 | - end | |
339 | - | |
340 | - def base_url | |
341 | - "#{protocol}://#{host}" | |
342 | - end | |
343 | - | |
344 | - def ssl? | |
345 | - protocol == 'https' | |
346 | - end | |
347 | - | |
348 | - def debug_output_io | |
349 | - super || STDERR | |
350 | - end | |
351 | - | |
352 | - def request_method | |
353 | - super || (data.empty? ? DEFAULT_REQUEST_METHOD : 'post') | |
354 | - end | |
355 | - | |
356 | - def protocol | |
357 | - super || DEFAULT_PROTOCOL | |
358 | - end | |
359 | - | |
360 | - def host | |
361 | - super || DEFAULT_HOST | |
362 | - end | |
363 | - | |
364 | - def proxy | |
365 | - super || nil | |
366 | - end | |
367 | - end | |
368 | -end |
tweeter_stream/lib/twurl.rb
... | ... | @@ -1,21 +0,0 @@ |
1 | -require 'rubygems' | |
2 | -require 'oauth' | |
3 | -require 'optparse' | |
4 | -require 'ostruct' | |
5 | -require 'stringio' | |
6 | -require 'yaml' | |
7 | - | |
8 | -library_files = Dir[File.join(File.dirname(__FILE__), "/twurl/**/*.rb")].sort | |
9 | -library_files.each do |file| | |
10 | - require file | |
11 | -end | |
12 | - | |
13 | -module Twurl | |
14 | - @options ||= Options.new | |
15 | - class << self | |
16 | - attr_accessor :options | |
17 | - end | |
18 | - | |
19 | - class Exception < ::Exception | |
20 | - end | |
21 | -end |
tweeter_stream/lib/twurl/abstract_command_controller.rb
... | ... | @@ -1,16 +0,0 @@ |
1 | -module Twurl | |
2 | - # Subclasses need to implement a `dispatch' instance method. | |
3 | - class AbstractCommandController | |
4 | - attr_reader :client, :options | |
5 | - class << self | |
6 | - def dispatch(*args, &block) | |
7 | - new(*args, &block).dispatch | |
8 | - end | |
9 | - end | |
10 | - | |
11 | - def initialize(client, options) | |
12 | - @client = client | |
13 | - @options = options | |
14 | - end | |
15 | - end | |
16 | -end |
tweeter_stream/lib/twurl/oauth_client.rb
... | ... | @@ -1,213 +0,0 @@ |
1 | -module Twurl | |
2 | - class OAuthClient | |
3 | - class << self | |
4 | - def rcfile(reload = false) | |
5 | - if reload || @rcfile.nil? | |
6 | - @rcfile = RCFile.new | |
7 | - end | |
8 | - @rcfile | |
9 | - end | |
10 | - | |
11 | - def load_from_options(options) | |
12 | - if rcfile.has_oauth_profile_for_username_with_consumer_key?(options.username, options.consumer_key) | |
13 | - load_client_for_username_and_consumer_key(options.username, options.consumer_key) | |
14 | - elsif options.username || (options.command == 'authorize') | |
15 | - load_new_client_from_options(options) | |
16 | - else | |
17 | - load_default_client | |
18 | - end | |
19 | - end | |
20 | - | |
21 | - def load_client_for_username_and_consumer_key(username, consumer_key) | |
22 | - user_profiles = rcfile[username] | |
23 | - if user_profiles && attributes = user_profiles[consumer_key] | |
24 | - new(attributes) | |
25 | - else | |
26 | - raise Exception, "No profile for #{username}" | |
27 | - end | |
28 | - end | |
29 | - | |
30 | - def load_client_for_username(username) | |
31 | - if user_profiles = rcfile[username] | |
32 | - if user_profiles.values.size == 1 | |
33 | - new(user_profiles.values.first) | |
34 | - else | |
35 | - raise Exception, "There is more than one consumer key associated with #{username}. Please specify which consumer key you want as well." | |
36 | - end | |
37 | - else | |
38 | - raise Exception, "No profile for #{username}" | |
39 | - end | |
40 | - end | |
41 | - | |
42 | - def load_new_client_from_options(options) | |
43 | - new(options.oauth_client_options.merge('password' => options.password, 'token' => options.access_token, 'secret' => options.token_secret)) | |
44 | - end | |
45 | - | |
46 | - def load_default_client | |
47 | - raise Exception, "You must authorize first" unless rcfile.default_profile | |
48 | - load_client_for_username_and_consumer_key(*rcfile.default_profile) | |
49 | - end | |
50 | - end | |
51 | - | |
52 | - OAUTH_CLIENT_OPTIONS = %w[username consumer_key consumer_secret token secret] | |
53 | - attr_reader *OAUTH_CLIENT_OPTIONS | |
54 | - attr_reader :username, :password | |
55 | - def initialize(options = {}) | |
56 | - @username = options['username'] | |
57 | - @password = options['password'] | |
58 | - @consumer_key = options['consumer_key'] | |
59 | - @consumer_secret = options['consumer_secret'] | |
60 | - @token = options['token'] | |
61 | - @secret = options['secret'] | |
62 | - configure_http! | |
63 | - end | |
64 | - | |
65 | - METHODS = { | |
66 | - :post => Net::HTTP::Post, | |
67 | - :get => Net::HTTP::Get, | |
68 | - :put => Net::HTTP::Put, | |
69 | - :delete => Net::HTTP::Delete, | |
70 | - :options => Net::HTTP::Options, | |
71 | - :head => Net::HTTP::Head, | |
72 | - :copy => Net::HTTP::Copy | |
73 | - } | |
74 | - | |
75 | - def perform_request_from_options(options, &block) | |
76 | - request_class = METHODS.fetch(options.request_method.to_sym) | |
77 | - request = request_class.new(options.path, options.headers) | |
78 | - | |
79 | - if options.upload && options.upload['file'].count > 0 | |
80 | - boundary = "00Twurl" + rand(1000000000000000000).to_s + "lruwT99" | |
81 | - multipart_body = [] | |
82 | - file_field = options.upload['filefield'] ? options.upload['filefield'] : 'media[]' | |
83 | - | |
84 | - options.data.each {|key, value| | |
85 | - multipart_body << "--#{boundary}\r\n" | |
86 | - multipart_body << "Content-Disposition: form-data; name=\"#{key}\"\r\n" | |
87 | - multipart_body << "\r\n" | |
88 | - multipart_body << value | |
89 | - multipart_body << "\r\n" | |
90 | - } | |
91 | - | |
92 | - options.upload['file'].each {|filename| | |
93 | - multipart_body << "--#{boundary}\r\n" | |
94 | - multipart_body << "Content-Disposition: form-data; name=\"#{file_field}\"; filename=\"#{File.basename(filename)}\"\r\n" | |
95 | - multipart_body << "Content-Type: application/octet-stream\r\n" | |
96 | - multipart_body << "Content-Transfer-Encoding: base64\r\n" if options.upload['base64'] | |
97 | - multipart_body << "\r\n" | |
98 | - | |
99 | - if options.upload['base64'] | |
100 | - enc = Base64.encode64(File.read(filename)) | |
101 | - multipart_body << enc | |
102 | - else | |
103 | - multipart_body << File.read(filename) | |
104 | - end | |
105 | - } | |
106 | - | |
107 | - multipart_body << "\r\n--#{boundary}--\r\n" | |
108 | - | |
109 | - request.body = multipart_body.join | |
110 | - request['Content-Type'] = "multipart/form-data, boundary=\"#{boundary}\"" | |
111 | - elsif options.data | |
112 | - request.set_form_data(options.data) | |
113 | - end | |
114 | - | |
115 | - request.oauth!(consumer.http, consumer, access_token) | |
116 | - consumer.http.read_timeout = options.read_timeout if options.read_timeout | |
117 | - consumer.http.request(request, &block) | |
118 | - end | |
119 | - | |
120 | - def exchange_credentials_for_access_token | |
121 | - response = begin | |
122 | - consumer.token_request(:post, consumer.access_token_path, nil, {}, client_auth_parameters) | |
123 | - rescue OAuth::Unauthorized | |
124 | - perform_pin_authorize_workflow | |
125 | - end | |
126 | - @token = response[:oauth_token] | |
127 | - @secret = response[:oauth_token_secret] | |
128 | - end | |
129 | - | |
130 | - def client_auth_parameters | |
131 | - {'x_auth_username' => username, 'x_auth_password' => password, 'x_auth_mode' => 'client_auth'} | |
132 | - end | |
133 | - | |
134 | - def perform_pin_authorize_workflow | |
135 | - @request_token = consumer.get_request_token | |
136 | - CLI.puts("Go to #{generate_authorize_url} and paste in the supplied PIN") | |
137 | - pin = gets | |
138 | - access_token = @request_token.get_access_token(:oauth_verifier => pin.chomp) | |
139 | - {:oauth_token => access_token.token, :oauth_token_secret => access_token.secret} | |
140 | - end | |
141 | - | |
142 | - def generate_authorize_url | |
143 | - request = consumer.create_signed_request(:get, consumer.authorize_path, @request_token, pin_auth_parameters) | |
144 | - params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map { |p| | |
145 | - k, v = p.split('=') | |
146 | - v =~ /"(.*?)"/ | |
147 | - "#{k}=#{CGI::escape($1)}" | |
148 | - }.join('&') | |
149 | - "#{Twurl.options.base_url}#{request.path}?#{params}" | |
150 | - end | |
151 | - | |
152 | - def pin_auth_parameters | |
153 | - {'oauth_callback' => 'oob'} | |
154 | - end | |
155 | - | |
156 | - def fetch_verify_credentials | |
157 | - access_token.get('/1.1/account/verify_credentials.json?include_entities=false&skip_status=true') | |
158 | - end | |
159 | - | |
160 | - def authorized? | |
161 | - oauth_response = fetch_verify_credentials | |
162 | - oauth_response.class == Net::HTTPOK | |
163 | - end | |
164 | - | |
165 | - def needs_to_authorize? | |
166 | - token.nil? || secret.nil? | |
167 | - end | |
168 | - | |
169 | - def save | |
170 | - verify_has_username | |
171 | - self.class.rcfile << self | |
172 | - end | |
173 | - | |
174 | - def verify_has_username | |
175 | - if username.nil? || username == '' | |
176 | - oauth_response = fetch_verify_credentials | |
177 | - oauth_response.body =~ /"screen_name"\s*:\s*"(.*?)"/ | |
178 | - @username = $1 | |
179 | - end | |
180 | - end | |
181 | - | |
182 | - def to_hash | |
183 | - OAUTH_CLIENT_OPTIONS.inject({}) do |hash, attribute| | |
184 | - if value = send(attribute) | |
185 | - hash[attribute] = value | |
186 | - end | |
187 | - hash | |
188 | - end | |
189 | - end | |
190 | - | |
191 | - def configure_http! | |
192 | - consumer.http.set_debug_output(Twurl.options.debug_output_io) if Twurl.options.trace | |
193 | - if Twurl.options.ssl? | |
194 | - consumer.http.use_ssl = true | |
195 | - consumer.http.verify_mode = OpenSSL::SSL::VERIFY_NONE | |
196 | - end | |
197 | - end | |
198 | - | |
199 | - def consumer | |
200 | - @consumer ||= | |
201 | - OAuth::Consumer.new( | |
202 | - consumer_key, | |
203 | - consumer_secret, | |
204 | - :site => Twurl.options.base_url, | |
205 | - :proxy => Twurl.options.proxy | |
206 | - ) | |
207 | - end | |
208 | - | |
209 | - def access_token | |
210 | - @access_token ||= OAuth::AccessToken.new(consumer, token, secret) | |
211 | - end | |
212 | - end | |
213 | -end |
tweeter_stream/lib/twurl/rcfile.rb
... | ... | @@ -1,103 +0,0 @@ |
1 | -module Twurl | |
2 | - class RCFile | |
3 | - | |
4 | - class << self | |
5 | - def directory | |
6 | - @@directory ||= File.expand_path('~') | |
7 | - end | |
8 | - | |
9 | - def directory=(dir) | |
10 | - @@directory = dir | |
11 | - end | |
12 | - | |
13 | - def file_path | |
14 | - Stream.file_path | |
15 | - end | |
16 | - | |
17 | - def load | |
18 | - YAML.load_file(file_path) | |
19 | - rescue Errno::ENOENT | |
20 | - default_rcfile_structure | |
21 | - end | |
22 | - | |
23 | - def default_rcfile_structure | |
24 | - {'profiles' => {}, 'configuration' => {}} | |
25 | - end | |
26 | - end | |
27 | - | |
28 | - attr_reader :data | |
29 | - def initialize | |
30 | - @data = self.class.load | |
31 | - end | |
32 | - | |
33 | - def set_path(p) | |
34 | - puts "entro set path #{p}" | |
35 | - @@file_path = p | |
36 | - end | |
37 | - | |
38 | - def empty? | |
39 | - data == self.class.default_rcfile_structure | |
40 | - end | |
41 | - | |
42 | - def save | |
43 | - File.open(self.class.file_path, File::RDWR|File::CREAT|File::TRUNC, 0600) do |rcfile| | |
44 | - rcfile.write data.to_yaml | |
45 | - end | |
46 | - end | |
47 | - | |
48 | - def [](username) | |
49 | - profiles[username] | |
50 | - end | |
51 | - | |
52 | - def profiles | |
53 | - data['profiles'] | |
54 | - end | |
55 | - | |
56 | - def default_profile | |
57 | - configuration['default_profile'] | |
58 | - end | |
59 | - | |
60 | - def default_profile=(profile) | |
61 | - configuration['default_profile'] = [profile.username, profile.consumer_key] | |
62 | - end | |
63 | - | |
64 | - def configuration | |
65 | - data['configuration'] | |
66 | - end | |
67 | - | |
68 | - def alias(name, path) | |
69 | - data['aliases'] ||= {} | |
70 | - data['aliases'][name] = path | |
71 | - save | |
72 | - end | |
73 | - | |
74 | - def aliases | |
75 | - data['aliases'] | |
76 | - end | |
77 | - | |
78 | - def alias_from_options(options) | |
79 | - options.subcommands.each do |potential_alias| | |
80 | - if path = alias_from_name(potential_alias) | |
81 | - break path | |
82 | - end | |
83 | - end | |
84 | - end | |
85 | - | |
86 | - def alias_from_name(name) | |
87 | - aliases[name] | |
88 | - end | |
89 | - | |
90 | - def has_oauth_profile_for_username_with_consumer_key?(username, consumer_key) | |
91 | - user_profiles = self[username] | |
92 | - !user_profiles.nil? && !user_profiles[consumer_key].nil? | |
93 | - end | |
94 | - | |
95 | - def <<(oauth_client) | |
96 | - client_from_file = self[oauth_client.username] || {} | |
97 | - client_from_file[oauth_client.consumer_key] = oauth_client.to_hash | |
98 | - (profiles[oauth_client.username] ||= {}).update(client_from_file) | |
99 | - self.default_profile = oauth_client unless default_profile | |
100 | - save | |
101 | - end | |
102 | - end | |
103 | -end |
tweeter_stream/lib/twurl/request_controller.rb
... | ... | @@ -1,62 +0,0 @@ |
1 | -require "json" | |
2 | -require 'iconv' | |
3 | - | |
4 | -module Twurl | |
5 | - class RequestController < AbstractCommandController | |
6 | - NO_URI_MESSAGE = "No URI specified" | |
7 | - | |
8 | - def dispatch | |
9 | - if client.needs_to_authorize? | |
10 | - raise Exception, "You need to authorize first." | |
11 | - end | |
12 | - options.path ||= OAuthClient.rcfile.alias_from_options(options) | |
13 | - perform_request | |
14 | - end | |
15 | - | |
16 | - def perform_request | |
17 | - client.perform_request_from_options(options) { |response| | |
18 | - chunk_begining = "" | |
19 | - puts "Connecting to tweeter stream: " + response.inspect | |
20 | - if response.inspect.to_s.include?('HTTPClientError 420') | |
21 | - puts "<<<<<<Error connecting to tweeter stream, maybe need another token!!!!!>>>>>>" | |
22 | - return | |
23 | - end | |
24 | - response.read_body { |chunk| | |
25 | - chunk = chunk_begining + chunk | |
26 | - chunk_complete = false | |
27 | - unless chunk.blank? | |
28 | - begin | |
29 | - parsed = JSON.parse(chunk) | |
30 | - chunk_complete = true | |
31 | - chunk_begining = "" | |
32 | - rescue JSON::ParserError => e | |
33 | - chunk_begining = chunk | |
34 | - chunk_complete = false | |
35 | - end | |
36 | - begin | |
37 | - if chunk_complete | |
38 | - ic = Iconv.new('UTF-8//IGNORE', 'UTF-8') | |
39 | - #Attention please, don't remove + ' ')[0..-2] it is used for UTF8 validation | |
40 | - comment_text = ic.iconv(parsed["text"] + ' ')[0..-2] | |
41 | - comment = Comment.new | |
42 | - comment.title = 'hub-message-twitter' | |
43 | - comment.source = options.page | |
44 | - comment.body = comment_text | |
45 | - comment.author_id = options.author_id | |
46 | - #Attention please, don't remove + ' ')[0..-2] it is used for UTF8 validation | |
47 | - comment.name = ic.iconv(parsed["user"]["name"] + ' ')[0..-2] | |
48 | - puts "@#{comment.name} " +_('said') + ": #{comment.body}" | |
49 | - comment.email = 'admin@localhost.local' | |
50 | - comment.save! | |
51 | - end | |
52 | - rescue => e | |
53 | - puts "Erro gravando comentário twitter #{e.inspect}" | |
54 | - end | |
55 | - end | |
56 | - } | |
57 | - } | |
58 | - rescue URI::InvalidURIError | |
59 | - Stream.puts NO_URI_MESSAGE | |
60 | - end | |
61 | - end | |
62 | -end | |
63 | 0 | \ No newline at end of file |
tweeter_stream/lib/twurl/sample.json
tweeter_stream/lib/twurl/stream.rb
... | ... | @@ -1,156 +0,0 @@ |
1 | -module Twurl | |
2 | - class Stream | |
3 | - | |
4 | - SUPPORTED_COMMANDS = %w(authorize accounts alias set) | |
5 | - DEFAULT_COMMAND = 'request' | |
6 | - PATH_PATTERN = /^\/\w+/ | |
7 | - PROTOCOL_PATTERN = /^\w+:\/\// | |
8 | - README = File.dirname(__FILE__) + '/../../README' | |
9 | - @output ||= STDOUT | |
10 | - class NoPathFound < Exception | |
11 | - end | |
12 | - | |
13 | - class << self | |
14 | - attr_accessor :output | |
15 | - def run(page, author_id, tags, config_file_path, proxy=nil) | |
16 | - begin | |
17 | - @@file_path = config_file_path | |
18 | - Twurl.options = Options.new | |
19 | - Twurl.options.command = 'request' # Not necessary anymore | |
20 | - Twurl.options.data = {"track"=>tags} | |
21 | -# Twurl.options.proxy = 'http://161.148.1.167:3128' # Use for production mode at SERPRO | |
22 | - Twurl.options.proxy = proxy unless proxy.nil? or proxy == '' | |
23 | - Twurl.options.trace = false | |
24 | - Twurl.options.headers = {} | |
25 | - Twurl.options.subcommands=[] | |
26 | - Twurl.options.upload = {} | |
27 | - Twurl.options.upload['file'] = [] | |
28 | - Twurl.options.path="/1.1/statuses/filter.json" | |
29 | - Twurl.options.host="stream.twitter.com" | |
30 | - Twurl.options.read_timeout= 0 | |
31 | - Twurl.options.page = page | |
32 | - Twurl.options.author_id = author_id | |
33 | - rescue NoPathFound => e | |
34 | - exit | |
35 | - end | |
36 | - dispatch(Twurl.options) | |
37 | - end | |
38 | - | |
39 | - def file_path | |
40 | - @@file_path | |
41 | - end | |
42 | - | |
43 | - def dispatch(options) | |
44 | - client = OAuthClient.load_from_options(options) | |
45 | - controller = RequestController | |
46 | - controller.dispatch(client, options) | |
47 | - rescue Twurl::Exception => exception | |
48 | - abort(exception.message) | |
49 | - end | |
50 | - | |
51 | - def output | |
52 | - if Twurl.options && Twurl.options.output | |
53 | - Twurl.options.output | |
54 | - else | |
55 | - @output | |
56 | - end | |
57 | - end | |
58 | - | |
59 | - def print(*args, &block) | |
60 | - output.print(*args, &block) | |
61 | - output.flush if output.respond_to?(:flush) | |
62 | - end | |
63 | - | |
64 | - def puts(*args, &block) | |
65 | - output.puts(*args, &block) | |
66 | - output.flush if output.respond_to?(:flush) | |
67 | - end | |
68 | - | |
69 | - def prompt_for(label) | |
70 | - system "stty -echo" | |
71 | - CLI.print "#{label}: " | |
72 | - result = STDIN.gets.chomp | |
73 | - CLI.puts | |
74 | - result | |
75 | - rescue Interrupt | |
76 | - exit | |
77 | - ensure | |
78 | - system "stty echo" | |
79 | - end | |
80 | - | |
81 | - private | |
82 | - def extract_command!(arguments) | |
83 | - if SUPPORTED_COMMANDS.include?(arguments.first) | |
84 | - arguments.shift | |
85 | - else | |
86 | - DEFAULT_COMMAND | |
87 | - end | |
88 | - end | |
89 | - | |
90 | - def extract_path!(arguments) | |
91 | - path = nil | |
92 | - arguments.each_with_index do |argument, index| | |
93 | - if argument[PATH_PATTERN] | |
94 | - path_with_params = arguments.slice!(index) | |
95 | - path, params = path_with_params.split("?", 2) | |
96 | - if params | |
97 | - path += "?" + escape_params(params) | |
98 | - end | |
99 | - break | |
100 | - end | |
101 | - end | |
102 | - path | |
103 | - end | |
104 | - | |
105 | - def escape_params(params) | |
106 | - split_params = params.split("&").map do |param| | |
107 | - key, value = param.split('=', 2) | |
108 | - CGI::escape(key) + '=' + CGI::escape(value) | |
109 | - end | |
110 | - split_params.join("&") | |
111 | - end | |
112 | - end | |
113 | - end | |
114 | - | |
115 | - | |
116 | - class Options < OpenStruct | |
117 | - DEFAULT_REQUEST_METHOD = 'get' | |
118 | - DEFAULT_HOST = 'api.twitter.com' | |
119 | - DEFAULT_PROTOCOL = 'https' | |
120 | - | |
121 | - def oauth_client_options | |
122 | - OAuthClient::OAUTH_CLIENT_OPTIONS.inject({}) do |options, option| | |
123 | - options[option] = send(option) | |
124 | - options | |
125 | - end | |
126 | - end | |
127 | - | |
128 | - def base_url | |
129 | - "#{protocol}://#{host}" | |
130 | - end | |
131 | - | |
132 | - def ssl? | |
133 | - protocol == 'https' | |
134 | - end | |
135 | - | |
136 | - def debug_output_io | |
137 | - super || STDERR | |
138 | - end | |
139 | - | |
140 | - def request_method | |
141 | - super || (data.empty? ? DEFAULT_REQUEST_METHOD : 'post') | |
142 | - end | |
143 | - | |
144 | - def protocol | |
145 | - super || DEFAULT_PROTOCOL | |
146 | - end | |
147 | - | |
148 | - def host | |
149 | - super || DEFAULT_HOST | |
150 | - end | |
151 | - | |
152 | - def proxy | |
153 | - super || nil | |
154 | - end | |
155 | - end | |
156 | -end |
tweeter_stream/lib/twurl/version.rb
... | ... | @@ -1,17 +0,0 @@ |
1 | -module Twurl | |
2 | - class Version | |
3 | - MAJOR = 0 unless defined? Twurl::Version::MAJOR | |
4 | - MINOR = 9 unless defined? Twurl::Version::MINOR | |
5 | - PATCH = 2 unless defined? Twurl::Version::PATCH | |
6 | - BETA = nil unless defined? Twurl::Version::BETA # Time.now.to_i.to_s | |
7 | - | |
8 | - class << self | |
9 | - # @return [String] | |
10 | - def to_s | |
11 | - [MAJOR, MINOR, PATCH, BETA].compact.join('.') | |
12 | - end | |
13 | - end | |
14 | - end | |
15 | - | |
16 | - VERSION = Version.to_s | |
17 | -end |
tweeter_stream/noosfero.rb
tweeter_stream/test/account_information_controller_test.rb
... | ... | @@ -1,61 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::AccountInformationController::DispatchWithNoAuthorizedAccountsTest < Minitest::Test | |
4 | - attr_reader :options, :client, :controller | |
5 | - def setup | |
6 | - @options = Twurl::Options.new | |
7 | - @client = Twurl::OAuthClient.load_new_client_from_options(options) | |
8 | - @controller = Twurl::AccountInformationController.new(client, options) | |
9 | - mock(Twurl::OAuthClient.rcfile).empty? { true } | |
10 | - end | |
11 | - | |
12 | - def test_message_indicates_when_no_accounts_are_authorized | |
13 | - mock(Twurl::CLI).puts(Twurl::AccountInformationController::NO_AUTHORIZED_ACCOUNTS_MESSAGE).times(1) | |
14 | - | |
15 | - controller.dispatch | |
16 | - end | |
17 | -end | |
18 | - | |
19 | -class Twurl::AccountInformationController::DispatchWithOneAuthorizedAccountTest < Minitest::Test | |
20 | - attr_reader :options, :client, :controller | |
21 | - def setup | |
22 | - @options = Twurl::Options.test_exemplar | |
23 | - @client = Twurl::OAuthClient.load_new_client_from_options(options) | |
24 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
25 | - Twurl::OAuthClient.rcfile << client | |
26 | - @controller = Twurl::AccountInformationController.new(client, options) | |
27 | - end | |
28 | - | |
29 | - def test_authorized_account_is_displayed_and_marked_as_the_default | |
30 | - mock(Twurl::CLI).puts(client.username).times(1).ordered | |
31 | - mock(Twurl::CLI).puts(" #{client.consumer_key} (default)").times(1).ordered | |
32 | - | |
33 | - controller.dispatch | |
34 | - end | |
35 | -end | |
36 | - | |
37 | -class Twurl::AccountInformationController::DispatchWithOneUsernameThatHasAuthorizedMultipleAccountsTest < Minitest::Test | |
38 | - attr_reader :default_client_options, :default_client, :other_client_options, :other_client, :controller | |
39 | - def setup | |
40 | - @default_client_options = Twurl::Options.test_exemplar | |
41 | - @default_client = Twurl::OAuthClient.load_new_client_from_options(default_client_options) | |
42 | - | |
43 | - @other_client_options = Twurl::Options.test_exemplar | |
44 | - other_client_options.consumer_key = default_client_options.consumer_key.reverse | |
45 | - @other_client = Twurl::OAuthClient.load_new_client_from_options(other_client_options) | |
46 | - mock(Twurl::OAuthClient.rcfile).save.times(2) | |
47 | - | |
48 | - Twurl::OAuthClient.rcfile << default_client | |
49 | - Twurl::OAuthClient.rcfile << other_client | |
50 | - | |
51 | - @controller = Twurl::AccountInformationController.new(other_client, other_client_options) | |
52 | - end | |
53 | - | |
54 | - def test_authorized_account_is_displayed_and_marked_as_the_default | |
55 | - mock(Twurl::CLI).puts(default_client.username).times(1) | |
56 | - mock(Twurl::CLI).puts(" #{default_client.consumer_key} (default)").times(1) | |
57 | - mock(Twurl::CLI).puts(" #{other_client.consumer_key}").times(1) | |
58 | - | |
59 | - controller.dispatch | |
60 | - end | |
61 | -end |
tweeter_stream/test/alias_controller_test.rb
... | ... | @@ -1,53 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::AliasesController::DispatchTest < Minitest::Test | |
4 | - attr_reader :options, :client | |
5 | - def setup | |
6 | - @options = Twurl::Options.test_exemplar | |
7 | - @client = Twurl::OAuthClient.test_exemplar | |
8 | - | |
9 | - # Clean slate | |
10 | - if Twurl::OAuthClient.rcfile.aliases | |
11 | - Twurl::OAuthClient.rcfile.aliases.clear | |
12 | - end | |
13 | - | |
14 | - stub(Twurl::OAuthClient.rcfile).save | |
15 | - end | |
16 | - | |
17 | - def test_when_no_subcommands_are_provided_and_no_aliases_exist_nothing_is_displayed | |
18 | - assert options.subcommands.empty? | |
19 | - mock(Twurl::CLI).puts(Twurl::AliasesController::NO_ALIASES_MESSAGE).times(1) | |
20 | - | |
21 | - controller = Twurl::AliasesController.new(client, options) | |
22 | - controller.dispatch | |
23 | - end | |
24 | - | |
25 | - def test_when_no_subcommands_are_provided_and_aliases_exist_they_are_displayed | |
26 | - assert options.subcommands.empty? | |
27 | - | |
28 | - Twurl::OAuthClient.rcfile.alias('h', '/1.1/statuses/home_timeline.json') | |
29 | - mock(Twurl::CLI).puts("h: /1.1/statuses/home_timeline.json").times(1) | |
30 | - | |
31 | - controller = Twurl::AliasesController.new(client, options) | |
32 | - controller.dispatch | |
33 | - end | |
34 | - | |
35 | - def test_when_alias_and_value_are_provided_they_are_added | |
36 | - options.subcommands = ['h'] | |
37 | - options.path = '/1.1/statuses/home_timeline.json' | |
38 | - mock(Twurl::OAuthClient.rcfile).alias('h', '/1.1/statuses/home_timeline.json').times(1) | |
39 | - | |
40 | - controller = Twurl::AliasesController.new(client, options) | |
41 | - controller.dispatch | |
42 | - end | |
43 | - | |
44 | - def test_when_no_path_is_provided_nothing_happens | |
45 | - options.subcommands = ['a'] | |
46 | - assert_nil options.path | |
47 | - | |
48 | - mock(Twurl::CLI).puts(Twurl::AliasesController::NO_PATH_PROVIDED_MESSAGE).times(1) | |
49 | - | |
50 | - controller = Twurl::AliasesController.new(client, options) | |
51 | - controller.dispatch | |
52 | - end | |
53 | -end |
tweeter_stream/test/authorization_controller_test.rb
... | ... | @@ -1,30 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::AuthorizationController::DispatchTest < Minitest::Test | |
4 | - attr_reader :options, :client, :controller | |
5 | - def setup | |
6 | - @options = Twurl::Options.new | |
7 | - @client = Twurl::OAuthClient.load_new_client_from_options(options) | |
8 | - @controller = Twurl::AuthorizationController.new(client, options) | |
9 | - end | |
10 | - | |
11 | - def test_successful_authentication_saves_retrieved_access_token | |
12 | - mock(client).exchange_credentials_for_access_token.times(1) | |
13 | - mock(client).save.times(1) | |
14 | - mock(controller).raise(Twurl::Exception, Twurl::AuthorizationController::AUTHORIZATION_FAILED_MESSAGE).never | |
15 | - mock(Twurl::CLI).puts(Twurl::AuthorizationController::AUTHORIZATION_SUCCEEDED_MESSAGE).times(1) | |
16 | - | |
17 | - controller.dispatch | |
18 | - end | |
19 | - | |
20 | - module ErrorCases | |
21 | - def test_failed_authorization_does_not_save_client | |
22 | - mock(client).exchange_credentials_for_access_token { raise OAuth::Unauthorized } | |
23 | - mock(client).save.never | |
24 | - mock(controller).raise(Twurl::Exception, Twurl::AuthorizationController::AUTHORIZATION_FAILED_MESSAGE).times(1) | |
25 | - | |
26 | - controller.dispatch | |
27 | - end | |
28 | - end | |
29 | - include ErrorCases | |
30 | -end |
tweeter_stream/test/cli_options_test.rb
... | ... | @@ -1,23 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::Options::Test < Minitest::Test | |
4 | - attr_reader :options | |
5 | - def setup | |
6 | - @options = Twurl::Options.new | |
7 | - end | |
8 | - | |
9 | - def test_base_url_is_built_from_protocol_and_host | |
10 | - options.protocol = 'http' | |
11 | - options.host = 'api.twitter.com' | |
12 | - | |
13 | - assert_equal 'http://api.twitter.com', options.base_url | |
14 | - end | |
15 | - | |
16 | - def test_ssl_is_enabled_if_the_protocol_is_https | |
17 | - options.protocol = 'http' | |
18 | - assert !options.ssl? | |
19 | - | |
20 | - options.protocol = 'https' | |
21 | - assert options.ssl? | |
22 | - end | |
23 | -end |
tweeter_stream/test/cli_test.rb
... | ... | @@ -1,192 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::CLI::OptionParsingTest < Minitest::Test | |
4 | - TEST_PATH = '/1.1/url/does/not/matter.json' | |
5 | - | |
6 | - module CommandParsingTests | |
7 | - def test_no_command_specified_falls_to_default_command | |
8 | - options = Twurl::CLI.parse_options([TEST_PATH]) | |
9 | - assert_equal Twurl::CLI::DEFAULT_COMMAND, options.command | |
10 | - end | |
11 | - | |
12 | - def test_supported_command_specified_extracts_the_command | |
13 | - expected_command = Twurl::CLI::SUPPORTED_COMMANDS.first | |
14 | - options = Twurl::CLI.parse_options([expected_command]) | |
15 | - assert_equal expected_command, options.command | |
16 | - end | |
17 | - | |
18 | - def test_unsupported_command_specified_sets_default_command | |
19 | - unsupported_command = 'unsupported' | |
20 | - options = Twurl::CLI.parse_options([TEST_PATH, unsupported_command]) | |
21 | - assert_equal Twurl::CLI::DEFAULT_COMMAND, options.command | |
22 | - end | |
23 | - end | |
24 | - include CommandParsingTests | |
25 | - | |
26 | - module PathParsingTests | |
27 | - def test_missing_path_throws_no_path_found | |
28 | - stub(Twurl::CLI).puts | |
29 | - assert_raises Twurl::CLI::NoPathFound do | |
30 | - Twurl::CLI.parse_options([]) | |
31 | - end | |
32 | - end | |
33 | - | |
34 | - def test_uri_params_are_encoded | |
35 | - options = Twurl::CLI.parse_options(["/1.1/url?baz=bamf:rofl"]) | |
36 | - assert_equal options.path, "/1.1/url?baz=bamf%3Arofl" | |
37 | - end | |
38 | - end | |
39 | - include PathParsingTests | |
40 | - | |
41 | - module RequestMethodParsingTests | |
42 | - def test_request_method_is_default_if_unspecified | |
43 | - options = Twurl::CLI.parse_options([TEST_PATH]) | |
44 | - assert_equal Twurl::Options::DEFAULT_REQUEST_METHOD, options.request_method | |
45 | - end | |
46 | - | |
47 | - def test_specifying_a_request_method_extracts_and_normalizes_request_method | |
48 | - variations = [%w[-X put], %w[-X PUT], %w[--request-method PUT], %w[--request-method put]] | |
49 | - variations.each do |option_variation| | |
50 | - order_variant_1 = [option_variation, TEST_PATH].flatten | |
51 | - order_variant_2 = [TEST_PATH, option_variation].flatten | |
52 | - [order_variant_1, order_variant_2].each do |args| | |
53 | - options = Twurl::CLI.parse_options(args) | |
54 | - assert_equal 'put', options.request_method | |
55 | - end | |
56 | - end | |
57 | - end | |
58 | - | |
59 | - def test_specifying_unsupported_request_method_returns_an_error | |
60 | - Twurl::CLI.parse_options([TEST_PATH, '-X', 'UNSUPPORTED']) | |
61 | - end | |
62 | - end | |
63 | - include RequestMethodParsingTests | |
64 | - | |
65 | - module OAuthClientOptionParsingTests | |
66 | - def test_extracting_the_consumer_key | |
67 | - mock(Twurl::CLI).prompt_for('Consumer key').never | |
68 | - | |
69 | - options = Twurl::CLI.parse_options([TEST_PATH, '-c', 'the-key']) | |
70 | - assert_equal 'the-key', options.consumer_key | |
71 | - end | |
72 | - | |
73 | - def test_consumer_key_option_with_no_value_prompts_user_for_value | |
74 | - mock(Twurl::CLI).prompt_for('Consumer key').times(1) { 'inputted-key'} | |
75 | - options = Twurl::CLI.parse_options([TEST_PATH, '-c']) | |
76 | - assert_equal 'inputted-key', options.consumer_key | |
77 | - end | |
78 | - end | |
79 | - include OAuthClientOptionParsingTests | |
80 | - | |
81 | - module DataParsingTests | |
82 | - def test_extracting_a_single_key_value_pair | |
83 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=value']) | |
84 | - assert_equal({'key' => 'value'}, options.data) | |
85 | - | |
86 | - options = Twurl::CLI.parse_options([TEST_PATH, '--data', 'key=value']) | |
87 | - assert_equal({'key' => 'value'}, options.data) | |
88 | - end | |
89 | - | |
90 | - def test_passing_data_and_no_explicit_request_method_defaults_request_method_to_post | |
91 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=value']) | |
92 | - assert_equal 'post', options.request_method | |
93 | - end | |
94 | - | |
95 | - def test_passing_data_and_an_explicit_request_method_uses_the_specified_method | |
96 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=value', '-X', 'DELETE']) | |
97 | - assert_equal({'key' => 'value'}, options.data) | |
98 | - assert_equal 'delete', options.request_method | |
99 | - end | |
100 | - | |
101 | - def test_multiple_pairs_when_option_is_specified_multiple_times_on_command_line_collects_all | |
102 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=value', '-d', 'another=pair']) | |
103 | - assert_equal({'key' => 'value', 'another' => 'pair'}, options.data) | |
104 | - end | |
105 | - | |
106 | - def test_multiple_pairs_separated_by_ampersand_are_all_captured | |
107 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=value&another=pair']) | |
108 | - assert_equal({'key' => 'value', 'another' => 'pair'}, options.data) | |
109 | - end | |
110 | - | |
111 | - def test_extracting_an_empty_key_value_pair | |
112 | - options = Twurl::CLI.parse_options([TEST_PATH, '-d', 'key=']) | |
113 | - assert_equal({'key' => ''}, options.data) | |
114 | - | |
115 | - options = Twurl::CLI.parse_options([TEST_PATH, '--data', 'key=']) | |
116 | - assert_equal({'key' => ''}, options.data) | |
117 | - end | |
118 | - end | |
119 | - include DataParsingTests | |
120 | - | |
121 | - module HeaderParsingTests | |
122 | - def test_extracting_a_single_header | |
123 | - options = Twurl::CLI.parse_options([TEST_PATH, '-A', 'Key: Value']) | |
124 | - assert_equal({'Key' => 'Value'}, options.headers) | |
125 | - | |
126 | - options = Twurl::CLI.parse_options([TEST_PATH, '--header', 'Key: Value']) | |
127 | - assert_equal({'Key' => 'Value'}, options.headers) | |
128 | - end | |
129 | - | |
130 | - def test_multiple_headers_when_option_is_specified_multiple_times_on_command_line_collects_all | |
131 | - options = Twurl::CLI.parse_options([TEST_PATH, '-A', 'Key: Value', '-A', 'Another: Pair']) | |
132 | - assert_equal({'Key' => 'Value', 'Another' => 'Pair'}, options.headers) | |
133 | - end | |
134 | - end | |
135 | - include HeaderParsingTests | |
136 | - | |
137 | - module SSLDisablingTests | |
138 | - def test_ssl_is_on_by_default | |
139 | - options = Twurl::CLI.parse_options([TEST_PATH]) | |
140 | - assert options.ssl? | |
141 | - end | |
142 | - | |
143 | - def test_passing_no_ssl_option_disables_ssl | |
144 | - ['-U', '--no-ssl'].each do |switch| | |
145 | - options = Twurl::CLI.parse_options([TEST_PATH, switch]) | |
146 | - assert !options.ssl? | |
147 | - end | |
148 | - end | |
149 | - end | |
150 | - include SSLDisablingTests | |
151 | - | |
152 | - module HostOptionTests | |
153 | - def test_not_specifying_host_sets_it_to_the_default | |
154 | - options = Twurl::CLI.parse_options([TEST_PATH]) | |
155 | - assert_equal Twurl::Options::DEFAULT_HOST, options.host | |
156 | - end | |
157 | - | |
158 | - def test_setting_host_updates_to_requested_value | |
159 | - custom_host = 'localhost:3000' | |
160 | - assert Twurl::Options::DEFAULT_HOST != custom_host | |
161 | - | |
162 | - [[TEST_PATH, '-H', custom_host], [TEST_PATH, '--host', custom_host]].each do |option_combination| | |
163 | - options = Twurl::CLI.parse_options(option_combination) | |
164 | - assert_equal custom_host, options.host | |
165 | - end | |
166 | - end | |
167 | - | |
168 | - def test_protocol_is_stripped_from_host | |
169 | - custom_host = 'localhost:3000' | |
170 | - options = Twurl::CLI.parse_options([TEST_PATH, '-H', "https://"+custom_host]) | |
171 | - assert_equal custom_host, options.host | |
172 | - end | |
173 | - end | |
174 | - include HostOptionTests | |
175 | - | |
176 | - module ProxyOptionTests | |
177 | - def test_not_specifying_proxy_sets_it_to_nil | |
178 | - options = Twurl::CLI.parse_options([TEST_PATH]) | |
179 | - assert_equal nil, options.proxy | |
180 | - end | |
181 | - | |
182 | - def test_setting_proxy_updates_to_requested_value | |
183 | - custom_proxy = 'localhost:80' | |
184 | - | |
185 | - [[TEST_PATH, '-P', custom_proxy], [TEST_PATH, '--proxy', custom_proxy]].each do |option_combination| | |
186 | - options = Twurl::CLI.parse_options(option_combination) | |
187 | - assert_equal custom_proxy, options.proxy | |
188 | - end | |
189 | - end | |
190 | - end | |
191 | - include ProxyOptionTests | |
192 | -end |
tweeter_stream/test/configuration_controller_test.rb
... | ... | @@ -1,44 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::ConfigurationController::DispatchTest < Minitest::Test | |
4 | - def test_error_message_is_displayed_if_setting_is_unrecognized | |
5 | - options = Twurl::Options.test_exemplar | |
6 | - client = Twurl::OAuthClient.test_exemplar | |
7 | - | |
8 | - options.subcommands = ['unrecognized', 'value'] | |
9 | - | |
10 | - mock(Twurl::CLI).puts(Twurl::ConfigurationController::UNRECOGNIZED_SETTING_MESSAGE % 'unrecognized').times(1) | |
11 | - mock(Twurl::OAuthClient.rcfile).save.times(0) | |
12 | - | |
13 | - controller = Twurl::ConfigurationController.new(client, options) | |
14 | - controller.dispatch | |
15 | - end | |
16 | -end | |
17 | - | |
18 | -class Twurl::ConfigurationController::DispatchDefaultSettingTest < Minitest::Test | |
19 | - def test_setting_default_profile_just_by_username | |
20 | - options = Twurl::Options.test_exemplar | |
21 | - client = Twurl::OAuthClient.test_exemplar | |
22 | - | |
23 | - options.subcommands = ['default', client.username] | |
24 | - mock(Twurl::OAuthClient).load_client_for_username(client.username).times(1) { client } | |
25 | - mock(Twurl::OAuthClient.rcfile).default_profile = client | |
26 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
27 | - | |
28 | - controller = Twurl::ConfigurationController.new(client, options) | |
29 | - controller.dispatch | |
30 | - end | |
31 | - | |
32 | - def test_setting_default_profile_by_username_and_consumer_key | |
33 | - options = Twurl::Options.test_exemplar | |
34 | - client = Twurl::OAuthClient.test_exemplar | |
35 | - | |
36 | - options.subcommands = ['default', client.username, client.consumer_key] | |
37 | - mock(Twurl::OAuthClient).load_client_for_username_and_consumer_key(client.username, client.consumer_key).times(1) { client } | |
38 | - mock(Twurl::OAuthClient.rcfile).default_profile = client | |
39 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
40 | - | |
41 | - controller = Twurl::ConfigurationController.new(client, options) | |
42 | - controller.dispatch | |
43 | - end | |
44 | -end |
tweeter_stream/test/oauth_client_test.rb
... | ... | @@ -1,165 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::OAuthClient::AbstractOAuthClientTest < Minitest::Test | |
4 | - attr_reader :client, :options | |
5 | - def setup | |
6 | - Twurl::OAuthClient.instance_variable_set(:@rcfile, nil) | |
7 | - | |
8 | - @options = Twurl::Options.test_exemplar | |
9 | - @client = Twurl::OAuthClient.test_exemplar | |
10 | - options.base_url = 'api.twitter.com' | |
11 | - options.request_method = 'get' | |
12 | - options.path = '/path/does/not/matter.json' | |
13 | - options.data = {} | |
14 | - options.headers = {} | |
15 | - | |
16 | - Twurl.options = options | |
17 | - end | |
18 | - | |
19 | - def teardown | |
20 | - super | |
21 | - Twurl.options = Twurl::Options.new | |
22 | - # Make sure we don't do any disk IO in these tests | |
23 | - assert !File.exists?(Twurl::RCFile.file_path) | |
24 | - end | |
25 | - | |
26 | - def test_nothing | |
27 | - # Appeasing test/unit | |
28 | - end | |
29 | -end | |
30 | - | |
31 | -class Twurl::OAuthClient::BasicRCFileLoadingTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
32 | - def test_rcfile_is_memoized | |
33 | - mock.proxy(Twurl::RCFile).new.times(1) | |
34 | - | |
35 | - Twurl::OAuthClient.rcfile | |
36 | - Twurl::OAuthClient.rcfile | |
37 | - end | |
38 | - | |
39 | - def test_forced_reloading | |
40 | - mock.proxy(Twurl::RCFile).new.times(2) | |
41 | - | |
42 | - Twurl::OAuthClient.rcfile | |
43 | - Twurl::OAuthClient.rcfile(:reload) | |
44 | - Twurl::OAuthClient.rcfile | |
45 | - end | |
46 | -end | |
47 | - | |
48 | -class Twurl::OAuthClient::ClientLoadingFromOptionsTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
49 | - def test_if_username_is_supplied_and_no_profile_exists_for_username_then_new_client_is_created | |
50 | - mock(Twurl::OAuthClient).load_client_for_username(options.username).never | |
51 | - mock(Twurl::OAuthClient).load_new_client_from_options(options).times(1) | |
52 | - mock(Twurl::OAuthClient).load_default_client.never | |
53 | - | |
54 | - Twurl::OAuthClient.load_from_options(options) | |
55 | - end | |
56 | - | |
57 | - def test_if_username_is_supplied_and_profile_exists_for_username_then_client_is_loaded | |
58 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
59 | - Twurl::OAuthClient.rcfile << client | |
60 | - | |
61 | - mock(Twurl::OAuthClient).load_client_for_username_and_consumer_key(options.username, options.consumer_key).times(1) | |
62 | - mock(Twurl::OAuthClient).load_new_client_from_options(options).never | |
63 | - mock(Twurl::OAuthClient).load_default_client.never | |
64 | - | |
65 | - Twurl::OAuthClient.load_from_options(options) | |
66 | - end | |
67 | - | |
68 | - def test_if_username_is_not_provided_then_the_default_client_is_loaded | |
69 | - options.username = nil | |
70 | - | |
71 | - mock(Twurl::OAuthClient).load_client_for_username(options.username).never | |
72 | - mock(Twurl::OAuthClient).load_new_client_from_options(options).never | |
73 | - mock(Twurl::OAuthClient).load_default_client.times(1) | |
74 | - | |
75 | - Twurl::OAuthClient.load_from_options(options) | |
76 | - end | |
77 | -end | |
78 | - | |
79 | -class Twurl::OAuthClient::ClientLoadingForUsernameTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
80 | - def test_attempting_to_load_a_username_that_is_not_in_the_file_fails | |
81 | - assert_nil Twurl::OAuthClient.rcfile[client.username] | |
82 | - | |
83 | - assert_raises Twurl::Exception do | |
84 | - Twurl::OAuthClient.load_client_for_username_and_consumer_key(client.username, client.consumer_key) | |
85 | - end | |
86 | - end | |
87 | - | |
88 | - def test_loading_a_username_that_exists | |
89 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
90 | - | |
91 | - Twurl::OAuthClient.rcfile << client | |
92 | - | |
93 | - client_from_file = Twurl::OAuthClient.load_client_for_username_and_consumer_key(client.username, client.consumer_key) | |
94 | - assert_equal client.to_hash, client_from_file.to_hash | |
95 | - end | |
96 | -end | |
97 | - | |
98 | -class Twurl::OAuthClient::DefaultClientLoadingTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
99 | - def test_loading_default_client_when_there_is_none_fails | |
100 | - assert_nil Twurl::OAuthClient.rcfile.default_profile | |
101 | - | |
102 | - assert_raises Twurl::Exception do | |
103 | - Twurl::OAuthClient.load_default_client | |
104 | - end | |
105 | - end | |
106 | - | |
107 | - def test_loading_default_client_from_file | |
108 | - mock(Twurl::OAuthClient.rcfile).save.times(1) | |
109 | - | |
110 | - Twurl::OAuthClient.rcfile << client | |
111 | - assert_equal [client.username, client.consumer_key], Twurl::OAuthClient.rcfile.default_profile | |
112 | - | |
113 | - client_from_file = Twurl::OAuthClient.load_default_client | |
114 | - | |
115 | - assert_equal client.to_hash, client_from_file.to_hash | |
116 | - end | |
117 | -end | |
118 | - | |
119 | -class Twurl::OAuthClient::NewClientLoadingFromOptionsTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
120 | - attr_reader :new_client | |
121 | - def setup | |
122 | - super | |
123 | - @new_client = Twurl::OAuthClient.load_new_client_from_options(options) | |
124 | - end | |
125 | - | |
126 | - def test_password_is_included | |
127 | - assert_equal options.password, new_client.password | |
128 | - end | |
129 | - | |
130 | - def test_oauth_options_are_passed_through | |
131 | - assert_equal client.to_hash, new_client.to_hash | |
132 | - end | |
133 | -end | |
134 | - | |
135 | -class Twurl::OAuthClient::PerformingRequestsFromOptionsTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
136 | - def test_request_is_made_using_request_method_and_path_and_data_in_options | |
137 | - client = Twurl::OAuthClient.test_exemplar | |
138 | - mock(client.consumer.http).request(satisfy { |req| | |
139 | - req.is_a?(Net::HTTP::Get) && (req.path == options.path) | |
140 | - }) | |
141 | - | |
142 | - client.perform_request_from_options(options) | |
143 | - end | |
144 | -end | |
145 | - | |
146 | -class Twurl::OAuthClient::CredentialsForAccessTokenExchangeTest < Twurl::OAuthClient::AbstractOAuthClientTest | |
147 | - def test_successful_exchange_parses_token_and_secret_from_response_body | |
148 | - parsed_response = {:oauth_token => "123456789", | |
149 | - :oauth_token_secret => "abcdefghi", | |
150 | - :user_id => "3191321", | |
151 | - :screen_name => "noradio", | |
152 | - :x_auth_expires => "0"} | |
153 | - | |
154 | - mock(client.consumer). | |
155 | - token_request(:post, | |
156 | - client.consumer.access_token_path, | |
157 | - nil, | |
158 | - {}, | |
159 | - client.client_auth_parameters) { parsed_response } | |
160 | - | |
161 | - assert client.needs_to_authorize? | |
162 | - client.exchange_credentials_for_access_token | |
163 | - assert !client.needs_to_authorize? | |
164 | - end | |
165 | -end |
tweeter_stream/test/rcfile_test.rb
... | ... | @@ -1,147 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::RCFile::PathConstructionTest < Minitest::Test | |
4 | - def test_file_path_appends_file_to_directory | |
5 | - assert_equal File.join(Twurl::RCFile.directory, Twurl::RCFile::FILE), Twurl::RCFile.file_path | |
6 | - end | |
7 | -end | |
8 | - | |
9 | -class Twurl::RCFile::LoadingTest < Minitest::Test | |
10 | - def test_load_parses_and_loads_file_if_it_exists | |
11 | - mock(YAML).load_file(Twurl::RCFile.file_path).times(1) | |
12 | - mock(Twurl::RCFile).default_rcfile_structure.never | |
13 | - | |
14 | - Twurl::RCFile.load | |
15 | - end | |
16 | - | |
17 | - def test_load_returns_default_file_structure_if_file_does_not_exist | |
18 | - mock(YAML).load_file(Twurl::RCFile.file_path) { raise Errno::ENOENT }.times(1) | |
19 | - mock(Twurl::RCFile).default_rcfile_structure.times(1) | |
20 | - | |
21 | - Twurl::RCFile.load | |
22 | - end | |
23 | -end | |
24 | - | |
25 | -class Twurl::RCFile::InitializationTest < Minitest::Test | |
26 | - def test_initializing_when_the_file_does_not_exist_loads_default_rcfile_structure | |
27 | - mock(YAML).load_file(Twurl::RCFile.file_path) { raise Errno::ENOENT }.times(1) | |
28 | - | |
29 | - rcfile = Twurl::RCFile.new | |
30 | - assert_equal Twurl::RCFile.default_rcfile_structure, rcfile.data | |
31 | - end | |
32 | - | |
33 | - def test_initializing_when_the_file_does_exists_loads_content_of_file | |
34 | - mock_content_of_rcfile = {'this data' => 'does not matter'} | |
35 | - mock(YAML).load_file(Twurl::RCFile.file_path) { mock_content_of_rcfile }.times(1) | |
36 | - mock(Twurl::RCFile).default_rcfile_structure.never | |
37 | - | |
38 | - rcfile = Twurl::RCFile.new | |
39 | - assert_equal mock_content_of_rcfile, rcfile.data | |
40 | - end | |
41 | -end | |
42 | - | |
43 | -class Twurl::RCFile::DefaultProfileFromDefaultRCFileTest < Minitest::Test | |
44 | - attr_reader :rcfile | |
45 | - def setup | |
46 | - mock(YAML).load_file(Twurl::RCFile.file_path) { raise Errno::ENOENT }.times(1) | |
47 | - mock.proxy(Twurl::RCFile).default_rcfile_structure.times(any_times) | |
48 | - | |
49 | - @rcfile = Twurl::RCFile.new | |
50 | - end | |
51 | - | |
52 | - def test_default_rcfile_structure_has_no_default_profile | |
53 | - assert_nil rcfile.default_profile | |
54 | - end | |
55 | - | |
56 | - def test_rcfile_is_considered_empty_at_first | |
57 | - assert rcfile.empty? | |
58 | - end | |
59 | - | |
60 | - def test_setting_default_profile | |
61 | - options = Twurl::Options.test_exemplar | |
62 | - | |
63 | - client = Twurl::OAuthClient.load_new_client_from_options(options) | |
64 | - rcfile.default_profile = client | |
65 | - assert_equal [options.username, options.consumer_key], rcfile.default_profile | |
66 | - end | |
67 | -end | |
68 | - | |
69 | -class Twurl::RCFile::UpdatingTest < Minitest::Test | |
70 | - attr_reader :rcfile | |
71 | - def setup | |
72 | - mock(YAML).load_file(Twurl::RCFile.file_path) { raise Errno::ENOENT }.times(1) | |
73 | - | |
74 | - @rcfile = Twurl::RCFile.new | |
75 | - assert rcfile.profiles.empty? | |
76 | - assert_nil rcfile.default_profile | |
77 | - mock(rcfile).save.times(any_times) | |
78 | - end | |
79 | - | |
80 | - def test_adding_the_first_client_sets_it_as_default_profile | |
81 | - client = Twurl::OAuthClient.test_exemplar | |
82 | - | |
83 | - rcfile << client | |
84 | - assert_equal [client.username, client.consumer_key], rcfile.default_profile | |
85 | - assert rcfile.has_oauth_profile_for_username_with_consumer_key?(client.username, client.consumer_key) | |
86 | - assert_equal({client.username => {client.consumer_key => client.to_hash}}, rcfile.profiles) | |
87 | - end | |
88 | - | |
89 | - def test_adding_additional_clients_does_not_change_default_profile | |
90 | - first_client = Twurl::OAuthClient.test_exemplar | |
91 | - | |
92 | - rcfile << first_client | |
93 | - assert_equal [first_client.username, first_client.consumer_key], rcfile.default_profile | |
94 | - assert rcfile.has_oauth_profile_for_username_with_consumer_key?(first_client.username, first_client.consumer_key) | |
95 | - | |
96 | - additional_client = Twurl::OAuthClient.test_exemplar(:username => 'additional_exemplar_username') | |
97 | - | |
98 | - rcfile << additional_client | |
99 | - assert_equal [first_client.username, first_client.consumer_key], rcfile.default_profile | |
100 | - assert rcfile.has_oauth_profile_for_username_with_consumer_key?(additional_client.username, additional_client.consumer_key) | |
101 | - | |
102 | - expected_profiles = { | |
103 | - first_client.username => {first_client.consumer_key => first_client.to_hash}, | |
104 | - additional_client.username => {additional_client.consumer_key => additional_client.to_hash} | |
105 | - } | |
106 | - | |
107 | - assert_equal expected_profiles, rcfile.profiles | |
108 | - end | |
109 | -end | |
110 | - | |
111 | -class Twurl::RCFile::SavingTest < Minitest::Test | |
112 | - attr_reader :rcfile | |
113 | - def setup | |
114 | - delete_rcfile | |
115 | - assert !rcfile_exists? | |
116 | - @rcfile = Twurl::RCFile.new | |
117 | - assert !rcfile_exists? | |
118 | - end | |
119 | - | |
120 | - def teardown | |
121 | - super | |
122 | - delete_rcfile | |
123 | - end | |
124 | - | |
125 | - def test_save_writes_profiles_to_disk | |
126 | - client = Twurl::OAuthClient.test_exemplar | |
127 | - rcfile << client | |
128 | - assert rcfile_exists? | |
129 | - end | |
130 | - | |
131 | - def test_file_is_not_world_readable | |
132 | - client = Twurl::OAuthClient.test_exemplar | |
133 | - rcfile << client | |
134 | - assert_equal 33152, File.stat(Twurl::RCFile.file_path).mode | |
135 | - end | |
136 | - | |
137 | - private | |
138 | - def rcfile_exists? | |
139 | - File.exists?(Twurl::RCFile.file_path) | |
140 | - end | |
141 | - | |
142 | - def delete_rcfile | |
143 | - File.unlink(Twurl::RCFile.file_path) | |
144 | - rescue Errno::ENOENT | |
145 | - # Do nothing | |
146 | - end | |
147 | -end |
tweeter_stream/test/request_controller_test.rb
... | ... | @@ -1,58 +0,0 @@ |
1 | -require File.dirname(__FILE__) + '/test_helper' | |
2 | - | |
3 | -class Twurl::RequestController::AbstractTestCase < Minitest::Test | |
4 | - attr_reader :options, :client, :controller | |
5 | - def setup | |
6 | - Twurl::CLI.output = StringIO.new | |
7 | - @options = Twurl::Options.test_exemplar | |
8 | - @client = Twurl::OAuthClient.test_exemplar | |
9 | - @controller = Twurl::RequestController.new(client, options) | |
10 | - end | |
11 | - | |
12 | - def teardown | |
13 | - super | |
14 | - Twurl::CLI.output = STDOUT | |
15 | - end | |
16 | - | |
17 | - def test_nothing | |
18 | - # Appeasing test/unit | |
19 | - end | |
20 | -end | |
21 | - | |
22 | -class Twurl::RequestController::DispatchTest < Twurl::RequestController::AbstractTestCase | |
23 | - def test_request_will_be_made_if_client_is_authorized | |
24 | - mock(client).needs_to_authorize? { false }.times(1) | |
25 | - mock(controller).perform_request.times(1) | |
26 | - | |
27 | - controller.dispatch | |
28 | - end | |
29 | - | |
30 | - def test_request_will_not_be_made_if_client_is_not_authorized | |
31 | - mock(client).needs_to_authorize? { true }.times(1) | |
32 | - mock(controller).perform_request.never | |
33 | - | |
34 | - assert_raises Twurl::Exception do | |
35 | - controller.dispatch | |
36 | - end | |
37 | - end | |
38 | -end | |
39 | - | |
40 | -class Twurl::RequestController::RequestTest < Twurl::RequestController::AbstractTestCase | |
41 | - def test_request_response_is_written_to_output | |
42 | - expected_body = 'this is a fake response body' | |
43 | - response = Object.new | |
44 | - mock(response).read_body { |block| block.call expected_body } | |
45 | - mock(client).perform_request_from_options(options).times(1) { |options, block| block.call(response) } | |
46 | - | |
47 | - controller.perform_request | |
48 | - | |
49 | - assert_equal expected_body, Twurl::CLI.output.string | |
50 | - end | |
51 | - | |
52 | - def test_invalid_or_unspecified_urls_report_error | |
53 | - mock(Twurl::CLI).puts(Twurl::RequestController::NO_URI_MESSAGE).times(1) | |
54 | - mock(client).perform_request_from_options(options).times(1) { raise URI::InvalidURIError } | |
55 | - | |
56 | - controller.perform_request | |
57 | - end | |
58 | -end |
tweeter_stream/test/test_helper.rb
... | ... | @@ -1,44 +0,0 @@ |
1 | -require 'simplecov' | |
2 | -require 'coveralls' | |
3 | - | |
4 | -SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ | |
5 | - SimpleCov::Formatter::HTMLFormatter, | |
6 | - Coveralls::SimpleCov::Formatter | |
7 | -] | |
8 | -SimpleCov.start | |
9 | - | |
10 | -require 'twurl' | |
11 | -require 'minitest/autorun' | |
12 | -require 'rr' | |
13 | - | |
14 | -Twurl::RCFile.directory = ENV['TMPDIR'] | |
15 | - | |
16 | -module Twurl | |
17 | - class Options | |
18 | - class << self | |
19 | - def test_exemplar | |
20 | - options = new | |
21 | - options.username = 'exemplar_user_name' | |
22 | - options.password = 'secret' | |
23 | - options.consumer_key = '123456789' | |
24 | - options.consumer_secret = '987654321' | |
25 | - options.subcommands = [] | |
26 | - options | |
27 | - end | |
28 | - end | |
29 | - end | |
30 | - | |
31 | - class OAuthClient | |
32 | - class << self | |
33 | - def test_exemplar(overrides = {}) | |
34 | - options = Twurl::Options.test_exemplar | |
35 | - | |
36 | - overrides.each do |attribute, value| | |
37 | - options.send("#{attribute}=", value) | |
38 | - end | |
39 | - | |
40 | - load_new_client_from_options(options) | |
41 | - end | |
42 | - end | |
43 | - end | |
44 | -end |
tweeter_stream/twurl.gemspec
... | ... | @@ -1,25 +0,0 @@ |
1 | -# coding: utf-8 | |
2 | -lib = File.expand_path('../lib', __FILE__) | |
3 | -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | |
4 | -require 'twurl/version' | |
5 | - | |
6 | -Gem::Specification.new do |spec| | |
7 | - spec.add_dependency 'oauth', '~> 0.4' | |
8 | - spec.add_development_dependency 'bundler', '~> 1.0' | |
9 | - spec.authors = ["Marcel Molina", "Erik Michaels-Ober"] | |
10 | - spec.description = %q{Curl for the Twitter API} | |
11 | - spec.email = ['marcel@twitter.com'] | |
12 | - spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } | |
13 | - spec.extra_rdoc_files = %w(COPYING INSTALL README) | |
14 | - spec.files = `git ls-files`.split("\n") | |
15 | - spec.homepage = 'http://github.com/twitter/twurl' | |
16 | - spec.licenses = ['MIT'] | |
17 | - spec.name = 'twurl' | |
18 | - spec.rdoc_options = ['--title', 'twurl -- OAuth-enabled curl for the Twitter API', '--main', 'README', '--line-numbers', '--inline-source'] | |
19 | - spec.require_paths = ['lib'] | |
20 | - spec.required_rubygems_version = '>= 1.3.5' | |
21 | - spec.rubyforge_project = 'twurl' | |
22 | - spec.summary = spec.description | |
23 | - spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") | |
24 | - spec.version = Twurl::Version | |
25 | -end |
... | ... | @@ -0,0 +1,54 @@ |
1 | +require 'rubygems' | |
2 | +require 'twitter' | |
3 | +require 'iconv' | |
4 | + | |
5 | +#Filters non-UTF8 octets | |
6 | +def UTF8Filter(string) | |
7 | + ic = Iconv.new('UTF-8//IGNORE', 'UTF-8') | |
8 | + #Attention please, don't remove + ' ')[0..-2] it is used for UTF8 validation | |
9 | + ic.iconv(string + ' ')[0..-2] | |
10 | +end | |
11 | + | |
12 | +def listen_twitter_stream(hub, author_id) | |
13 | + connected = false | |
14 | + while !connected | |
15 | + begin | |
16 | + client = Twitter::Streaming::Client.new do |config| | |
17 | + config.consumer_key = hub.twitter_consumer_key | |
18 | + config.consumer_secret = hub.twitter_consumer_secret | |
19 | + config.access_token = hub.twitter_access_token | |
20 | + config.access_token_secret = hub.twitter_access_token_secret | |
21 | + end | |
22 | + connected = true | |
23 | + rescue => e | |
24 | + puts "Error connecting to twitter stream: #{e.inspect}" | |
25 | + sleep 10 | |
26 | + end | |
27 | + end | |
28 | + while true | |
29 | + begin | |
30 | + client.filter(:track => hub.twitter_hashtags) do |object| | |
31 | + if object.is_a?(Twitter::Tweet) | |
32 | + puts '@' + object.user.screen_name + ' said: ' + object.text | |
33 | +# puts object.user.profile_image_url | |
34 | + comment = Comment.new | |
35 | + comment.title = 'hub-message-twitter' | |
36 | + comment.source = hub | |
37 | + comment.body = UTF8Filter(object.text) | |
38 | + comment.author_id = author_id | |
39 | + comment.name = UTF8Filter(object.user.screen_name) | |
40 | + comment.email = 'admin@localhost.local' | |
41 | + begin | |
42 | + comment.save! | |
43 | + rescue => e | |
44 | + puts "Erro gravando comentário twitter #{e.inspect}" | |
45 | + end | |
46 | + end | |
47 | + end | |
48 | + rescue => e | |
49 | + puts "Erro lendo stream #{e.inspect}" | |
50 | + sleep 10 | |
51 | + break | |
52 | + end | |
53 | + end | |
54 | +end | |
0 | 55 | \ No newline at end of file | ... | ... |
views/cms/community_hub_plugin/_hub.rhtml
... | ... | @@ -9,7 +9,6 @@ |
9 | 9 | <%= required labelled_form_field(_('Description'), text_area(:article, 'body', :style => 'width: 100%;', :class => 'mceEditor')) %> |
10 | 10 | </div> |
11 | 11 | <br /> |
12 | - | |
13 | 12 | <div> |
14 | 13 | <%= _('General Streaming Settings:') %> |
15 | 14 | </div> |
... | ... | @@ -19,8 +18,21 @@ |
19 | 18 | </div> |
20 | 19 | <br /> |
21 | 20 | <%= check_box(:article, :twitter_enabled) %> <span><%= _("Turn on TWITTER") %></span> |
22 | - <br /><br /> | |
23 | - <span><%= required labelled_form_field(_('Twitter\'s Hashtags, comma separated words<br>(example: participa.br,participabr,arenanetmundial,netmundial'), text_field(:article, :hashtags_twitter)) %></span> | |
21 | + <br /> | |
22 | + <br /> | |
23 | + <span><%= required labelled_form_field(_('Twitter\'s Hashtags, comma separated words<br>(example: participa.br,participabr,arenanetmundial,netmundial'), text_field(:article, :twitter_hashtags)) %></span> | |
24 | + <br /> | |
25 | + <br /> | |
26 | + <span><%= required labelled_form_field(_('Twitter\'s consumer key'), text_field(:article, :twitter_consumer_key)) %></span> | |
27 | + <br /> | |
28 | + <br /> | |
29 | + <span><%= required labelled_form_field(_('Twitter\'s consumer secret'), text_field(:article, :twitter_consumer_secret)) %></span> | |
30 | + <br /> | |
31 | + <br /> | |
32 | + <span><%= required labelled_form_field(_('Twitter\'s access token'), text_field(:article, :twitter_access_token)) %></span> | |
33 | + <br /> | |
34 | + <br /> | |
35 | + <span><%= required labelled_form_field(_('Twitter\'s access token secret'), text_field(:article, :twitter_access_token_secret)) %></span> | |
24 | 36 | <br /> |
25 | 37 | <div> |
26 | 38 | <%= _('Facebook Settings:') %> | ... | ... |