rbitter 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -21
  3. data/.rspec +2 -2
  4. data/.travis.yml +15 -15
  5. data/Gemfile +12 -12
  6. data/LICENSE.txt +22 -22
  7. data/Rakefile +8 -8
  8. data/XMLRPC.md +19 -19
  9. data/bin/rbitter +20 -20
  10. data/lib/rbitter/arcserver.rb +130 -126
  11. data/lib/rbitter/console.rb +93 -93
  12. data/lib/rbitter/default/config_json.rb +37 -38
  13. data/lib/rbitter/dlthread.rb +62 -65
  14. data/lib/rbitter/env.rb +62 -62
  15. data/lib/rbitter/libtwitter_connection_override.rb +45 -45
  16. data/lib/rbitter/records.rb +121 -121
  17. data/lib/rbitter/records_migrate/20150327_add_index.rb +11 -11
  18. data/lib/rbitter/records_migrate/20150504_add_replyto_column.rb +11 -11
  19. data/lib/rbitter/streaming.rb +104 -104
  20. data/lib/rbitter/version.rb +20 -20
  21. data/lib/rbitter/xmlrpc.rb +3 -3
  22. data/lib/rbitter/xmlrpcd/base.rb +24 -24
  23. data/lib/rbitter/xmlrpcd/rpchandles.rb +11 -11
  24. data/lib/rbitter/xmlrpcd/xmlrpc_auth_server.rb +82 -82
  25. data/lib/rbitter/xmlrpcd/xmlrpcd.rb +69 -69
  26. data/lib/rbitter.rb +86 -86
  27. data/rbitter.gemspec +46 -42
  28. data/spec/config/default.json +32 -32
  29. data/spec/rbitter/arcserver_spec.rb +30 -30
  30. data/spec/rbitter/console_spec.rb +9 -9
  31. data/spec/rbitter/default/config_json_spec.rb +3 -3
  32. data/spec/rbitter/dlthread_spec.rb +8 -13
  33. data/spec/rbitter/env_spec.rb +62 -62
  34. data/spec/rbitter/libtwitter_connection_override_spec.rb +8 -8
  35. data/spec/rbitter/records_spec.rb +13 -13
  36. data/spec/rbitter/streaming_spec.rb +9 -9
  37. data/spec/rbitter/version_spec.rb +8 -8
  38. data/spec/rbitter/xmlrpc_spec.rb +8 -8
  39. data/spec/rbitter/xmlrpcd/base_spec.rb +29 -29
  40. data/spec/rbitter/xmlrpcd/rpchandles_spec.rb +10 -10
  41. data/spec/rbitter/xmlrpcd/xmlrpc_auth_server_spec.rb +8 -8
  42. data/spec/rbitter/xmlrpcd/xmlrpcd_spec.rb +9 -9
  43. data/spec/rbitter_spec.rb +42 -42
  44. data/spec/spec_helper.rb +39 -39
  45. metadata +29 -56
@@ -1,93 +1,93 @@
1
- # encoding: utf-8
2
- #
3
- # Rbitter Archive Access console (irb)
4
-
5
- require "xmlrpc/client"
6
- require "rbitter/version"
7
- require "ripl"
8
-
9
- module Rbitter
10
- class Console
11
- def initialize
12
- puts "Rbitter console #{Rbitter::VERSION}"
13
- help
14
- end
15
-
16
- def help
17
- puts "Predefined methods:"
18
- puts "ar - shortcut to call Rbitter::Record"
19
- puts "connect_ar - Prepare Rbitter::Record to be ready"
20
- puts "csv_backup - export Rbitter::Record into comma-separated values"
21
- puts "help - to show this message again"
22
- puts "xmlrpc - send xmlrpc command to destination"
23
- puts "xmlrpc_dest - set destination for xmlrpc command"
24
- puts "^D, 'exit' to exit from here"
25
- end
26
-
27
- def connect_ar
28
- ARSupport.connect_database
29
- puts "Rbitter::Record is ready."
30
- end
31
-
32
- def csv_backup *args
33
- if args.length < 0
34
- puts "Usage: csv_backup('filename.csv')"
35
- puts "Estimated running time depends on system environment"
36
- else
37
- ARSupport.export_to_csv(args[0])
38
- end
39
- end
40
-
41
- def ar
42
- Rbitter::Record
43
- end
44
-
45
- def exit
46
- Kernel.exit(0)
47
- end
48
-
49
- def xmlrpc_dest args={}
50
- if args.empty?
51
- puts "Usage: xmlrpc_dest({ :rpchost => '', :rpcpath => '', :rpcport => 1400,"
52
- puts " :xmlrpc_auth_id => '', xmlrpc_auth_password => '' })"
53
- end
54
-
55
- @rpchost = args.fetch(:rpchost) { "127.0.0.1" }
56
- @rpcpath = args.fetch(:rpcpath) { "/" }
57
- @rpcport = args.fetch(:rpcport) { 1400 }
58
-
59
- cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport)
60
- @xmlrpc_cookie = "auth_key=" + cl.call('rbitter.auth',
61
- args.fetch(:xmlrpc_auth_id) { Rbitter.env['xmlrpc']['auth'][0] },
62
- args.fetch(:xmlrpc_auth_password) { Rbitter.env['xmlrpc']['auth'][1] } )
63
-
64
- if @xmlrpc_cookie != "auth_key="
65
- puts "Authentication completed"
66
- else
67
- puts "Authentication failed"
68
- end
69
- end
70
-
71
- def xmlrpc *args
72
- if args.empty?
73
- puts "Usage: xmlrpc(command, [params in Array])"
74
- puts "Ex) xmlrpc(\'rbitter.echo\',' [\"Hello World!\"])"
75
- puts "Please configure XMLRPC destination with xmlrpc_dest method"
76
- return false
77
- end
78
-
79
- cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport, @xmlrpc_cookie)
80
-
81
- if args.length <= 1 or args[1].nil?
82
- cl.call(args[0])
83
- else
84
- cl.call(args[0], *args[1])
85
- end
86
- end
87
-
88
- def start
89
- Ripl.start :binding => binding
90
- end
91
- end
92
- end
93
-
1
+ # encoding: utf-8
2
+ #
3
+ # Rbitter Archive Access console (irb)
4
+
5
+ require "xmlrpc/client"
6
+ require "rbitter/version"
7
+ require "ripl"
8
+
9
+ module Rbitter
10
+ class Console
11
+ def initialize
12
+ puts "Rbitter console #{Rbitter::VERSION}"
13
+ help
14
+ end
15
+
16
+ def help
17
+ puts "Predefined methods:"
18
+ puts "ar - shortcut to call Rbitter::Record"
19
+ puts "connect_ar - Prepare Rbitter::Record to be ready"
20
+ puts "csv_backup - export Rbitter::Record into comma-separated values"
21
+ puts "help - to show this message again"
22
+ puts "xmlrpc - send xmlrpc command to destination"
23
+ puts "xmlrpc_dest - set destination for xmlrpc command"
24
+ puts "^D, 'exit' to exit from here"
25
+ end
26
+
27
+ def connect_ar
28
+ ARSupport.connect_database
29
+ puts "Rbitter::Record is ready."
30
+ end
31
+
32
+ def csv_backup *args
33
+ if args.length < 0
34
+ puts "Usage: csv_backup('filename.csv')"
35
+ puts "Estimated running time depends on system environment"
36
+ else
37
+ ARSupport.export_to_csv(args[0])
38
+ end
39
+ end
40
+
41
+ def ar
42
+ Rbitter::Record
43
+ end
44
+
45
+ def exit
46
+ Kernel.exit(0)
47
+ end
48
+
49
+ def xmlrpc_dest args={}
50
+ if args.empty?
51
+ puts "Usage: xmlrpc_dest({ :rpchost => '', :rpcpath => '', :rpcport => 1400,"
52
+ puts " :xmlrpc_auth_id => '', xmlrpc_auth_password => '' })"
53
+ end
54
+
55
+ @rpchost = args.fetch(:rpchost) { "127.0.0.1" }
56
+ @rpcpath = args.fetch(:rpcpath) { "/" }
57
+ @rpcport = args.fetch(:rpcport) { 1400 }
58
+
59
+ cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport)
60
+ @xmlrpc_cookie = "auth_key=" + cl.call('rbitter.auth',
61
+ args.fetch(:xmlrpc_auth_id) { Rbitter.env['xmlrpc']['auth'][0] },
62
+ args.fetch(:xmlrpc_auth_password) { Rbitter.env['xmlrpc']['auth'][1] } )
63
+
64
+ if @xmlrpc_cookie != "auth_key="
65
+ puts "Authentication completed"
66
+ else
67
+ puts "Authentication failed"
68
+ end
69
+ end
70
+
71
+ def xmlrpc *args
72
+ if args.empty?
73
+ puts "Usage: xmlrpc(command, [params in Array])"
74
+ puts "Ex) xmlrpc(\'rbitter.echo\',' [\"Hello World!\"])"
75
+ puts "Please configure XMLRPC destination with xmlrpc_dest method"
76
+ return false
77
+ end
78
+
79
+ cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport, @xmlrpc_cookie)
80
+
81
+ if args.length <= 1 or args[1].nil?
82
+ cl.call(args[0])
83
+ else
84
+ cl.call(args[0], *args[1])
85
+ end
86
+ end
87
+
88
+ def start
89
+ Ripl.start :binding => binding
90
+ end
91
+ end
92
+ end
93
+
@@ -1,38 +1,37 @@
1
- module Rbitter
2
- DEFAULT_CONFIG_JSON = <<-ENDOFJSON
3
- {
4
- "twitter": {
5
- "consumer_key": "",
6
- "consumer_secret": "",
7
- "access_token": "",
8
- "access_token_secret": ""
9
- },
10
- "activerecord": "sqlite3",
11
- "sqlite3": {
12
- "dbfile": "rbitter.sqlite"
13
- },
14
- "mysql2": {
15
- "host": "localhost",
16
- "port": 3306,
17
- "dbname": "archive",
18
- "username": "",
19
- "password": ""
20
- },
21
- "media_downloader": {
22
- "large_image": true,
23
- "cacert_path": "/cacerts/cacert.pem",
24
- "download_dir": "imgs/"
25
- },
26
- "xmlrpc": {
27
- "enable": true,
28
- "bind_host": "0.0.0.0",
29
- "bind_port": 1400,
30
- "auth": {
31
- "username": "username",
32
- "password": "password"
33
- },
34
- "handles": ["/path/to/handles"]
35
- }
36
- }
37
- ENDOFJSON
38
- end
1
+ module Rbitter
2
+ DEFAULT_CONFIG_JSON = <<-ENDOFJSON
3
+ {
4
+ "twitter": {
5
+ "consumer_key": "",
6
+ "consumer_secret": "",
7
+ "access_token": "",
8
+ "access_token_secret": ""
9
+ },
10
+ "activerecord": "sqlite3",
11
+ "sqlite3": {
12
+ "dbfile": "rbitter.sqlite"
13
+ },
14
+ "mysql2": {
15
+ "host": "localhost",
16
+ "port": 3306,
17
+ "dbname": "archive",
18
+ "username": "",
19
+ "password": ""
20
+ },
21
+ "media_downloader": {
22
+ "large_image": true,
23
+ "download_dir": "imgs/"
24
+ },
25
+ "xmlrpc": {
26
+ "enable": true,
27
+ "bind_host": "0.0.0.0",
28
+ "bind_port": 1400,
29
+ "auth": {
30
+ "username": "username",
31
+ "password": "password"
32
+ },
33
+ "handles": ["/path/to/handles"]
34
+ }
35
+ }
36
+ ENDOFJSON
37
+ end
@@ -1,66 +1,63 @@
1
- # encoding: utf-8
2
-
3
- require "net/http"
4
- require "openssl"
5
-
6
- module Rbitter
7
- class DLThread
8
- def initialize(dlfolder, cacert_path, large_flag)
9
- @dest = dlfolder
10
- if not File.directory?(dlfolder)
11
- warn "[dlthread] Given download location is not available for downloading."
12
- warn "[dlthread] Fallback to current directory."
13
- @dest = "./"
14
- end
15
-
16
- @cacert = cacert_path
17
- if large_flag.nil?
18
- @large_image = false
19
- else
20
- @large_image = large_flag
21
- end
22
-
23
- @pool = Array.new
24
- end
25
-
26
- def <<(url_array)
27
- download_task = Thread.new {
28
- url_array.each { |url|
29
- uri = URI.parse(@large_image ? url + ":large" : url)
30
- http = Net::HTTP.new(uri.host, uri.port)
31
- if uri.scheme.downcase == 'https'
32
- http.use_ssl = true
33
- http.ca_path = @cacert
34
- end
35
-
36
- http.request_get(uri.path) { |res|
37
- case res
38
- when Net::HTTPOK
39
- fname = File.basename(url)
40
-
41
- puts "[fetch] remote: #{uri.path} => local: #{fname}"
42
- open(File.join(@dest, fname), "wb") { |file|
43
- res.read_body { |chunk| file.write(chunk) }
44
- }
45
- end
46
- }
47
- }
48
- }
49
-
50
- @pool.push download_task
51
- end
52
-
53
- def job_cleanup
54
- until @pool.empty?
55
- puts "[dlthread] Thread forceful cleaning up [remains: #{@pool.length}]"
56
-
57
- dlthrd = @pool.shift
58
-
59
- if dlthrd.alive?
60
- dlthrd.terminate
61
- dlthrd.join
62
- end
63
- end
64
- end
65
- end
1
+ # encoding: utf-8
2
+
3
+ require "net/http"
4
+ require "openssl"
5
+
6
+ module Rbitter
7
+ class DLThread
8
+ def initialize(dlfolder, large_flag)
9
+ @dest = dlfolder
10
+ if not File.directory?(dlfolder)
11
+ warn "[dlthread] Given download location is not available for downloading."
12
+ warn "[dlthread] Fallback to current directory."
13
+ @dest = "./"
14
+ end
15
+
16
+ if large_flag.nil?
17
+ @large_image = false
18
+ else
19
+ @large_image = large_flag
20
+ end
21
+
22
+ @pool = Array.new
23
+ end
24
+
25
+ def <<(url_array)
26
+ download_task = Thread.new {
27
+ url_array.each { |url|
28
+ uri = URI.parse(@large_image ? url + ":large" : url)
29
+ ssl = uri.scheme.downcase == 'https'
30
+
31
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl) { |h|
32
+ req = Net::HTTP::Get.new uri.request_uri
33
+ h.request(req) { |res|
34
+ case res
35
+ when Net::HTTPOK
36
+ fname = File.basename(url)
37
+
38
+ puts "[fetch] remote: #{uri.path} => local: #{fname}"
39
+ open(File.join(@dest, fname), "wb") { |file|
40
+ res.read_body { |chunk| file.write(chunk) }
41
+ }
42
+ end
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ @pool.push download_task
49
+ end
50
+
51
+ def job_cleanup
52
+ until @pool.empty?
53
+ dlthrd = @pool.shift
54
+
55
+ if dlthrd.alive?
56
+ puts "[dlthread] Thread forceful cleaning up [remains: #{@pool.length}]"
57
+ dlthrd.terminate
58
+ dlthrd.join
59
+ end
60
+ end
61
+ end
62
+ end
66
63
  end
data/lib/rbitter/env.rb CHANGED
@@ -1,62 +1,62 @@
1
- # encoding: utf-8
2
-
3
- require "json"
4
-
5
- module Rbitter
6
- @@env = Hash.new
7
-
8
- class ConfigFileError < StandardError; end
9
-
10
- def self.[](k)
11
- @@env[k]
12
- end
13
-
14
- module_function
15
- def env
16
- @@env
17
- end
18
-
19
- def env_reset
20
- @@env.clear
21
- end
22
-
23
- def env_validate?
24
- # TODO: Add validator
25
- true
26
- end
27
-
28
- def config_initialize json_path=nil
29
- env_reset
30
-
31
- unless json_path.nil?
32
- begin
33
- open(json_path, 'r') { |file|
34
- @@env = JSON.parse(file.read)
35
- }
36
-
37
- return @@env if env_validate?
38
- fail StandardError, "Invalid configuration"
39
- rescue => e
40
- fail ConfigFileError, "Load Failure (#{json_path}): #{e.to_s}"
41
- end
42
- end
43
-
44
- # Configuration default location
45
- # 1. (current_dir)/config.json
46
- # 2. (current_dir)/.rbitter/config.json
47
- locations = ["config.json", ".rbitter/config.json"]
48
- locations.collect! { |base| File.join(Dir.pwd, base) }
49
-
50
- for location in locations
51
- next unless File.file?(location)
52
- open(location, 'r') { |file|
53
- @@env = JSON.parse(file.read)
54
- }
55
- break if env_validate?
56
- end
57
-
58
- if @@env.empty?
59
- fail ConfigFileError, "Can not load any configuration in [#{locations.join(', ')}]"
60
- end
61
- end
62
- end
1
+ # encoding: utf-8
2
+
3
+ require "json"
4
+
5
+ module Rbitter
6
+ @@env = Hash.new
7
+
8
+ class ConfigFileError < StandardError; end
9
+
10
+ def self.[](k)
11
+ @@env[k]
12
+ end
13
+
14
+ module_function
15
+ def env
16
+ @@env
17
+ end
18
+
19
+ def env_reset
20
+ @@env.clear
21
+ end
22
+
23
+ def env_validate?
24
+ # TODO: Add validator
25
+ true
26
+ end
27
+
28
+ def config_initialize json_path=nil
29
+ env_reset
30
+
31
+ unless json_path.nil?
32
+ begin
33
+ open(json_path, 'r') { |file|
34
+ @@env = JSON.parse(file.read)
35
+ }
36
+
37
+ return @@env if env_validate?
38
+ fail StandardError, "Invalid configuration"
39
+ rescue => e
40
+ fail ConfigFileError, "Load Failure (#{json_path}): #{e.to_s}"
41
+ end
42
+ end
43
+
44
+ # Configuration default location
45
+ # 1. (current_dir)/config.json
46
+ # 2. (current_dir)/.rbitter/config.json
47
+ locations = ["config.json", ".rbitter/config.json"]
48
+ locations.collect! { |base| File.join(Dir.pwd, base) }
49
+
50
+ for location in locations
51
+ next unless File.file?(location)
52
+ open(location, 'r') { |file|
53
+ @@env = JSON.parse(file.read)
54
+ }
55
+ break if env_validate?
56
+ end
57
+
58
+ if @@env.empty?
59
+ fail ConfigFileError, "Can not load any configuration in [#{locations.join(', ')}]"
60
+ end
61
+ end
62
+ end
@@ -1,46 +1,46 @@
1
- # encoding: utf-8
2
-
3
- require 'http/parser'
4
- require 'openssl'
5
- require 'resolv'
6
-
7
- module Twitter
8
- module Streaming
9
- class Connection
10
- MODIFIED = true
11
- attr_reader :tcp_socket_class, :ssl_socket_class
12
-
13
- def initialize(options = {})
14
- @tcp_socket_class = options.fetch(:tcp_socket_class) { TCPSocket }
15
- @ssl_socket_class = options.fetch(:ssl_socket_class) { OpenSSL::SSL::SSLSocket }
16
- end
17
-
18
- def stream(request, response)
19
- client_context = OpenSSL::SSL::SSLContext.new
20
- client = @tcp_socket_class.new(Resolv.getaddress(request.uri.host), request.uri.port)
21
- ssl_client = @ssl_socket_class.new(client, client_context)
22
- ssl_client.connect
23
- request.stream(ssl_client)
24
-
25
- loop {
26
- begin
27
- body = ssl_client.read_nonblock(1024) # rubocop:disable AssignmentInCondition, WhileUntilModifier
28
- response << body
29
- rescue IO::WaitReadable
30
- # The reason for setting 90 seconds as a timeout is documented on:
31
- # https://dev.twitter.com/streaming/overview/connecting
32
- r, w, e = IO.select([ssl_client], [], [], 90)
33
- if r.nil?
34
- # If timeout occurs
35
- ssl_client.close
36
- raise Twitter::Error::ServerError.new("Connection stalled")
37
- else
38
- # If socket is readable
39
- retry
40
- end
41
- end
42
- }
43
- end
44
- end
45
- end
1
+ # encoding: utf-8
2
+
3
+ require 'http/parser'
4
+ require 'openssl'
5
+ require 'resolv'
6
+
7
+ module Twitter
8
+ module Streaming
9
+ class Connection
10
+ MODIFIED = true
11
+ attr_reader :tcp_socket_class, :ssl_socket_class
12
+
13
+ def initialize(options = {})
14
+ @tcp_socket_class = options.fetch(:tcp_socket_class) { TCPSocket }
15
+ @ssl_socket_class = options.fetch(:ssl_socket_class) { OpenSSL::SSL::SSLSocket }
16
+ end
17
+
18
+ def stream(request, response)
19
+ client_context = OpenSSL::SSL::SSLContext.new
20
+ client = @tcp_socket_class.new(Resolv.getaddress(request.uri.host), request.uri.port)
21
+ ssl_client = @ssl_socket_class.new(client, client_context)
22
+ ssl_client.connect
23
+ request.stream(ssl_client)
24
+
25
+ loop {
26
+ begin
27
+ body = ssl_client.read_nonblock(1024) # rubocop:disable AssignmentInCondition, WhileUntilModifier
28
+ response << body
29
+ rescue IO::WaitReadable
30
+ # The reason for setting 90 seconds as a timeout is documented on:
31
+ # https://dev.twitter.com/streaming/overview/connecting
32
+ r, w, e = IO.select([ssl_client], [], [], 90)
33
+ if r.nil?
34
+ # If timeout occurs
35
+ ssl_client.close
36
+ raise Twitter::Error::ServerError.new("Connection stalled")
37
+ else
38
+ # If socket is readable
39
+ retry
40
+ end
41
+ end
42
+ }
43
+ end
44
+ end
45
+ end
46
46
  end