rbitter 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ca2dd0e8a08659416b4e1ba154375673bfe697d
4
- data.tar.gz: d60daa351fef6fdfa8cc3fdb444b31e10a820c80
3
+ metadata.gz: c258b870ebf4e8dd9304ce2cfc588cf720fba7c1
4
+ data.tar.gz: 0a86c5194600ef1964ec08ae923e8abd9a6b7740
5
5
  SHA512:
6
- metadata.gz: 251d4bda5d89583701ca2ecb3b52c5cafeab42b6e3f01d90692e2457d48f53e22ff9cda790daed72b63088cd077c4ba7ba27e8dd0751e9d2b5f1dbbf68dd1286
7
- data.tar.gz: cb04a2013000e2581b5b8c6dd5b30b15058c6f70f995fbde2dbf31733e78a23c7b8526cafc203cae9d0125f16752c8728cc45b3103f28a83e2d52dbac153118e
6
+ metadata.gz: 211b5214c71b22df1d0d0f944760bc8f3c2518e1e9552d044e72c3606bb4fa158422da7330406ecd8885a0ddcf6defe416606176efa75ed16836c9f316cefb6a
7
+ data.tar.gz: d6f73a9ca34b941c0de31a4153a0b9144425dd0279b699f9aa60ff35c6bcbe312799f03ec6ff3c62d2021bc1ed380885aa072437cfa723e115d686c486f9dcbc
data/.gitignore CHANGED
@@ -17,5 +17,6 @@ mkmf.log
17
17
  *.gem
18
18
  *.json
19
19
  *.sqlite
20
+ *.png
20
21
  .rbitter
21
22
  .rbitter/*
data/README.md CHANGED
@@ -30,6 +30,11 @@ Put your customized config.json to one of below locations.
30
30
  1. ./config.json (current folder)
31
31
  2. ./.rbitter/config.json (current folder)
32
32
 
33
+ ### XMLRPC handlers ###
34
+ To get XMLRPC handlers, visit [Rbitter XMLRPC](https://github.com/nidev/rbitter-rpchandles).
35
+
36
+ You can find the document to write your own XMLRPC handler.
37
+
33
38
  ## Set up and run ##
34
39
  With config.json,
35
40
 
@@ -42,9 +47,6 @@ With config.json,
42
47
  ```bash
43
48
  $ rbitter serve
44
49
  ```
45
- ## XMLRPC API ##
46
-
47
- See XMLRPC.md
48
50
 
49
51
  ## TODO ##
50
52
  * Streaming Client
data/lib/rbitter.rb CHANGED
@@ -6,6 +6,7 @@ require "rbitter/arcserver"
6
6
  require "rbitter/env"
7
7
  require "rbitter/console"
8
8
  require "rbitter/xmlrpc"
9
+ require "rbitter/override"
9
10
 
10
11
  module Rbitter
11
12
  BOOTSTRAP_ARGS = ['configure', 'console', 'help', 'logs', 'serve']
@@ -26,29 +27,6 @@ module Rbitter
26
27
  puts "`- logs : Show Rbitter internal logs"
27
28
  end
28
29
 
29
- def self.prebootstrap
30
- # Due to stalled socket problem, If unpatched twitter gem is installed.
31
- # Twitter::Streaming::Connection will be monkey-patched.
32
- patch_required = false
33
-
34
- if Twitter::Version.const_defined?(:MAJOR)
35
- b5_version = Twitter::Version::MAJOR * 10000
36
- + Twitter::Version::MINOR * 100 + Twitter::Version::PATCH
37
- if b5_version <= 51400
38
- warn "[rbitter] Monkey-patching Twitter::Streaming::Connection"
39
- warn "[rbitter] Please upgrade twitter gem to apply streaming read timeout"
40
- patch_required = true
41
- end
42
- else
43
- b6_version = Twitter::Version.to_a
44
- if b6_version[0] <= 6 and b6_version[1] <= 0 and b6_version[2] <= 0
45
- patch_required = true
46
- end
47
- end
48
-
49
- require "rbitter/libtwitter_connection_override" if patch_required
50
- end
51
-
52
30
  def self.bootstrap_configs
53
31
  require "rbitter/default/config_json"
54
32
 
@@ -61,8 +39,6 @@ module Rbitter
61
39
  return nil if args.length < 1
62
40
 
63
41
  if args[0] == "serve"
64
- prebootstrap
65
-
66
42
  Rbitter.config_initialize
67
43
 
68
44
  archive_server = Rbitter::ArcServer.new
@@ -15,10 +15,16 @@ module Rbitter
15
15
  LOG_NORMAL = 0
16
16
  LOG_INIT = 1
17
17
  LOG_HALT = 2
18
+ LOG_ERROR = 4
18
19
 
19
20
  def initialize(xmlrpcd_class = Rbitter::RPCServer)
20
21
  @xmlrpcd_class = xmlrpcd_class
22
+ @dt = DLThread.new(
23
+ Rbitter['media_downloader']['download_dir'],
24
+ Rbitter['media_downloader']['large_image'])
25
+ end
21
26
 
27
+ def arsupport_init
22
28
  ARSupport.connect_database
23
29
 
24
30
  if not ARSupport.prepared?
@@ -31,10 +37,10 @@ module Rbitter
31
37
  end
32
38
 
33
39
  ARSupport.update_database_scheme
40
+ end
34
41
 
35
- @dt = DLThread.new(
36
- Rbitter['media_downloader']['download_dir'],
37
- Rbitter['media_downloader']['large_image'])
42
+ def arsupport_halt
43
+ ARSupport.disconnect_database
38
44
  end
39
45
 
40
46
  def xmlrpcd_start
@@ -72,21 +78,38 @@ module Rbitter
72
78
  :fav_count => 0})
73
79
  end
74
80
 
75
- def write_init_marker
81
+ def mark_init
76
82
  mark(LOG_INIT, "Archiving service started")
77
83
  end
78
84
 
79
- def write_halt_marker
85
+ def mark_halt
80
86
  mark(LOG_HALT, "Archiving service halted")
81
87
  end
82
88
 
89
+ def mark_error(exception_string, err_msg)
90
+ mark(LOG_ERROR, "Errored (#{exception_string}, #{err_msg}")
91
+ end
92
+
93
+ def resurrect_loop?
94
+ if Rbitter.env['twitter']['connection']['reconnect']
95
+ puts "[rbitter] Try to reconnect..."
96
+ sleep Rbitter.env['twitter']['connection']['timeout_secs']
97
+ true
98
+ else
99
+ puts "[rbitter] Give up!"
100
+ false
101
+ end
102
+ end
103
+
83
104
  def main_loop(streaming_adapter = Rbitter::StreamClient)
84
105
  xmlrpcd_start if Rbitter['xmlrpc']['enable']
85
106
 
107
+ arsupport_init
108
+
86
109
  begin
87
- write_init_marker
110
+ mark_init
88
111
 
89
- streaming_adapter.new(Rbitter['twitter'].dup).run { |a|
112
+ streaming_adapter.new(Rbitter['twitter']).run { |a|
90
113
  @dt << a['media_urls']
91
114
 
92
115
  record = Record.find_or_initialize_by(tweetid: a['tweetid'])
@@ -106,25 +129,37 @@ module Rbitter
106
129
  rescue Interrupt => e
107
130
  puts ""
108
131
  puts "Interrupted..."
132
+ mark_error(e.to_s, "(exit) SIGINT - interrupted by user")
109
133
  rescue Twitter::Error::Unauthorized => e
110
134
  warn "Twitter access unauthorized:"
111
135
  warn " Possible solutions"
112
136
  warn " 1. Configure Twitter token on config.json"
113
137
  warn " 2. Check system time (Time is important on authentication)"
114
138
  warn " 3. Check Twitter account status"
115
- rescue Twitter::Error::ServerError, Resolv::ResolvError => e
116
- puts "Service unavailable now. Retry in 5 second..."
117
- sleep 5
118
- retry
139
+ rescue Twitter::Error::ServerError => e
140
+ puts "Service unavailable now. Retry in 5 seconds..."
141
+ mark_error(e.to_s, "(retry) Twitter server unavailable / Timeout")
142
+
143
+ retry if resurrect_loop?
144
+ rescue Resolv::ResolvError, Errno::ECONNABORTED,
145
+ Errno::ECONNREFUSED, Errno::ECONNRESET => e
146
+ puts "Network problem. Retry in 5 seconds..."
147
+ mark_error(e.to_s, "(retry) Network problem")
148
+
149
+ retry if resurrect_loop?
119
150
  rescue Twitter::Error => e
120
151
  warn "Twitter Error: #{e.inspect}"
121
- warn "Main loop of ArcServer halted."
152
+ warn "Rbitter halts due to Twitter::Error"
153
+ mark_error(e.to_s, "(exit) Twitter Error")
122
154
  ensure
123
155
  xmlrpcd_stop if Rbitter['xmlrpc']['enable']
124
156
  @dt.job_cleanup
125
157
 
126
- write_halt_marker
158
+ mark_halt
127
159
  end
160
+
161
+ arsupport_halt
128
162
  end
163
+
129
164
  end
130
165
  end
@@ -5,7 +5,11 @@ module Rbitter
5
5
  "consumer_key": "",
6
6
  "consumer_secret": "",
7
7
  "access_token": "",
8
- "access_token_secret": ""
8
+ "access_token_secret": "",
9
+ "connection": {
10
+ "reconnect": true,
11
+ "timeout_secs": 5
12
+ }
9
13
  },
10
14
  "activerecord": "sqlite3",
11
15
  "sqlite3": {
data/lib/rbitter/env.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require "json"
4
+ require "rbitter/default/config_json"
4
5
 
5
6
  module Rbitter
6
7
  @@env = Hash.new
7
8
 
8
9
  class ConfigFileError < StandardError; end
10
+ class MissingFieldError < StandardError; end
9
11
 
10
12
  def self.[](k)
11
13
  @@env[k]
@@ -20,9 +22,60 @@ module Rbitter
20
22
  @@env.clear
21
23
  end
22
24
 
23
- def env_validate?
24
- # TODO: Add validator
25
- true
25
+ def env_listfields hash
26
+ path_stack = ['']
27
+ generated = []
28
+
29
+ until path_stack.empty?
30
+ path = path_stack.pop
31
+
32
+ if path == ''
33
+ o = hash
34
+ else
35
+ nodes = path.strip.split('->')
36
+ o = hash
37
+ until nodes.empty?
38
+ o = o[nodes.shift]
39
+ end
40
+ end
41
+
42
+ o.each_key { |k|
43
+ if o[k].is_a?(Hash)
44
+ path_stack << "#{k}" if path.empty?
45
+ path_stack << path + "->#{k}" unless path.empty?
46
+ else
47
+ generated << "#{k}" if path.empty?
48
+ generated << path + "->#{k}" unless path.empty?
49
+ end
50
+ }
51
+ end
52
+
53
+ generated
54
+ end
55
+
56
+ def env_valid?
57
+ defaults = env_listfields(JSON.parse(DEFAULT_CONFIG_JSON))
58
+ currents = env_listfields(@@env)
59
+ not_errored = true
60
+
61
+ # Cross checking (2 phases)
62
+ # In current exists, default does not: redundant configuration
63
+ # Level: warning since it is not utilized at all.
64
+ currents.each { |conf|
65
+ unless defaults.include?(conf)
66
+ warn "[config.json] Unused config: #{conf}. You can safely remove it."
67
+ end
68
+ }
69
+
70
+ # In default exists, current does not: missing configuration
71
+ # Level: error and program should stop. (return false for this)
72
+ defaults.each { |conf|
73
+ unless currents.include?(conf)
74
+ warn "[config.json] Config not found: #{conf}. Invalid configuration!"
75
+ not_errored = false
76
+ end
77
+ }
78
+ not_errored
26
79
  end
27
80
 
28
81
  def config_initialize json_path=nil
@@ -34,7 +87,7 @@ module Rbitter
34
87
  @@env = JSON.parse(file.read)
35
88
  }
36
89
 
37
- return @@env if env_validate?
90
+ return @@env if env_valid?
38
91
  fail StandardError, "Invalid configuration"
39
92
  rescue => e
40
93
  fail ConfigFileError, "Load Failure (#{json_path}): #{e.to_s}"
@@ -52,11 +105,12 @@ module Rbitter
52
105
  open(location, 'r') { |file|
53
106
  @@env = JSON.parse(file.read)
54
107
  }
55
- break if env_validate?
108
+ break unless @@env.empty?
56
109
  end
57
110
 
58
- if @@env.empty?
59
- fail ConfigFileError, "Can not load any configuration in [#{locations.join(', ')}]"
60
- end
111
+ fail ConfigFileError, "No config.json on #{locations.join(' or ')}" if @@env.empty?
112
+ fail ConfigFileError, "Configuration outdated. Please see above messages to update it" if not env_valid?
113
+
114
+ puts "[config.json] Loaded configuration is valid. good to go!"
61
115
  end
62
116
  end
@@ -0,0 +1,47 @@
1
+ =begin
2
+ This module gives special workarounds for some issues.
3
+
4
+ Problems are bypassed by monkey-patching.
5
+
6
+ As soon as a problem is resolved, patch should be removed.
7
+
8
+ **** Known issues ****
9
+
10
+ 1. gem/twitter
11
+ Location : override/gem/twitter/connection.rb
12
+ Maintain : until below issue is fixed
13
+ Reference: https://github.com/sferik/twitter/pull/669
14
+
15
+ Gem 'twitter' does not handle streaming timeout. By applying this,
16
+ reading timeout works and Rbitter can handle timeouts.
17
+
18
+ 2. gem/rubysl-socket
19
+ Location : override/gem/rubysl-socket/socket.rb
20
+ Maintain : until ip_address_list is implemented
21
+ Reference: https://github.com/rubysl/rubysl-socket/pull/9
22
+
23
+ With ipv6 environment, Resolv#use_ipv6? (in rubysl-resolv gem) checks
24
+ Socket.ip_address_list. This is not implemented at all with rubysl-socket-2.0.1.
25
+ NoMethodError exception is raised instead of NotImplementedError.
26
+
27
+ By applying this, Socket.ip_address_list is implemented and the method throws
28
+ NotImplementedError exception.
29
+
30
+ =end
31
+
32
+ def gem_twitter_patcher
33
+ require 'rbitter/override/gems/twitter/connection'
34
+ end
35
+
36
+ if Twitter::Version.const_defined?(:MAJOR)
37
+ b5_version = Twitter::Version::MAJOR * 10000
38
+ + Twitter::Version::MINOR * 100 + Twitter::Version::PATCH
39
+ gem_twitter_patcher if b5_version <= 51400
40
+ else
41
+ b6_version = Twitter::Version.to_a
42
+ if b6_version[0] <= 6 and b6_version[1] <= 0 and b6_version[2] <= 0
43
+ gem_twitter_patcher
44
+ end
45
+ end
46
+
47
+ require 'rbitter/override/gems/rubysl-socket/socket' if RUBY_ENGINE == 'rbx'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ require 'socket'
3
+
4
+ class Socket
5
+ def self.ip_address_list
6
+ fail NotImplementedError
7
+ end
8
+ end
@@ -63,6 +63,12 @@ module ARSupport
63
63
  end
64
64
  end
65
65
 
66
+ def disconnect_database
67
+ if ActiveRecord::Base.connected?
68
+ ActiveRecord::Base.connection.close
69
+ end
70
+ end
71
+
66
72
  def update_database_scheme
67
73
  current_version = ActiveRecord::Migrator.current_version
68
74
  if current_version < SCHEME_VERSION
@@ -1,6 +1,6 @@
1
1
  module Rbitter
2
2
  PRODUCT_NAME = "Rbitter"
3
- VERSION = "0.1.2"
3
+ VERSION = "0.2.0"
4
4
 
5
5
  def major
6
6
  VERSION.match(/^([0-9]+)\./)[1]
@@ -53,10 +53,24 @@ describe Rbitter do
53
53
  end
54
54
  end
55
55
 
56
- context 'when env_validator validates loaded configuration' do
57
- # TODO: Perform test with spec/config/default.json
58
- # TODO: Adding configuration validator on env.rb
59
- end
56
+ context "when 'env_valid?' validates loaded configuration" do
57
+ before(:all) do
58
+ Rbitter.bootstrap(['configure'])
59
+ expect(File.file?('config.json')).to be(true)
60
+ end
61
+
62
+ it 'gets \'false\' for empty configuration' do
63
+ Rbitter.env_reset
64
+ expect(Rbitter.env_valid?).to be(false)
65
+ end
60
66
 
61
- # TODO: Perform test with spec/config/default.json
67
+ it "gets \'true\' with default and another default" do
68
+ Rbitter.config_initialize
69
+ expect(Rbitter.env_valid?).to be(true)
70
+ end
71
+
72
+ after(:all) do
73
+ File.delete('config.json')
74
+ end
75
+ end
62
76
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- require "rbitter/libtwitter_connection_override"
2
+ require "rbitter/override/gems/twitter/connection"
3
3
 
4
4
  describe Rbitter do
5
5
  it 'overrides twitter gem' do
data/spec/rbitter_spec.rb CHANGED
@@ -22,10 +22,6 @@ describe Rbitter do
22
22
  Rbitter.bootstrap(['configure'])
23
23
  end
24
24
 
25
- it 'executes prebootstrap properly' do
26
- Rbitter.prebootstrap
27
- end
28
-
29
25
  it 'bootstraps nothing when empty ARGV is given' do
30
26
  Rbitter.bootstrap([])
31
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nidev Plontra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-17 00:00:00.000000000 Z
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: twitter
@@ -152,7 +152,6 @@ files:
152
152
  - LICENSE.txt
153
153
  - README.md
154
154
  - Rakefile
155
- - XMLRPC.md
156
155
  - bin/rbitter
157
156
  - lib/rbitter.rb
158
157
  - lib/rbitter/arcserver.rb
@@ -160,7 +159,9 @@ files:
160
159
  - lib/rbitter/default/config_json.rb
161
160
  - lib/rbitter/dlthread.rb
162
161
  - lib/rbitter/env.rb
163
- - lib/rbitter/libtwitter_connection_override.rb
162
+ - lib/rbitter/override.rb
163
+ - lib/rbitter/override/gems/rubysl-socket/socket.rb
164
+ - lib/rbitter/override/gems/twitter/connection.rb
164
165
  - lib/rbitter/records.rb
165
166
  - lib/rbitter/records_migrate/.keep
166
167
  - lib/rbitter/records_migrate/20150327_add_index.rb
@@ -180,7 +181,8 @@ files:
180
181
  - spec/rbitter/default/config_json_spec.rb
181
182
  - spec/rbitter/dlthread_spec.rb
182
183
  - spec/rbitter/env_spec.rb
183
- - spec/rbitter/libtwitter_connection_override_spec.rb
184
+ - spec/rbitter/override/gems/rubysl-socket/socket_spec.rb
185
+ - spec/rbitter/override/gems/twitter/connection_spec.rb
184
186
  - spec/rbitter/records_spec.rb
185
187
  - spec/rbitter/streaming_spec.rb
186
188
  - spec/rbitter/version_spec.rb
@@ -224,7 +226,8 @@ test_files:
224
226
  - spec/rbitter/default/config_json_spec.rb
225
227
  - spec/rbitter/dlthread_spec.rb
226
228
  - spec/rbitter/env_spec.rb
227
- - spec/rbitter/libtwitter_connection_override_spec.rb
229
+ - spec/rbitter/override/gems/rubysl-socket/socket_spec.rb
230
+ - spec/rbitter/override/gems/twitter/connection_spec.rb
228
231
  - spec/rbitter/records_spec.rb
229
232
  - spec/rbitter/streaming_spec.rb
230
233
  - spec/rbitter/version_spec.rb
data/XMLRPC.md DELETED
@@ -1,19 +0,0 @@
1
- # What is RPC handle ? #
2
- ## Commands ##
3
- ### Authentication ###
4
- ### Revoke Authentication Token ###
5
- ### Echo ###
6
- ### Last Active ###
7
- ### Retriever ###
8
- ### Statistic ###
9
- # How to write own RPC handle? #
10
- RPC handle is a Ruby class. Writing a method in Ruby class, that's it. Names of methods are treated as XMLRPC command.
11
-
12
- When you write a new class for your own RPC handle, you must inherit either Auth or NoAuth class from rpc/base.rb.
13
-
14
- * class Auth < Object: Methods in a Ruby class inheriting Auth requires *auth_key* to access.
15
- * class NoAuth < Object: Methods in a Ruby class inheriting NoAuth doesn't require *auth_key* and these XMLRPC commands can be called by anonymous user.
16
-
17
- Filename should start with 'rh_'. It's prefix to be autoloaded by xmlrpc.rb.
18
-
19
- Refer rpc/rh_echo.rb as an example.