rbitter 0.1.2-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +8 -0
- data/XMLRPC.md +19 -0
- data/bin/rbitter +20 -0
- data/lib/rbitter.rb +86 -0
- data/lib/rbitter/arcserver.rb +130 -0
- data/lib/rbitter/console.rb +93 -0
- data/lib/rbitter/default/config_json.rb +37 -0
- data/lib/rbitter/dlthread.rb +63 -0
- data/lib/rbitter/env.rb +62 -0
- data/lib/rbitter/libtwitter_connection_override.rb +46 -0
- data/lib/rbitter/records.rb +121 -0
- data/lib/rbitter/records_migrate/.keep +0 -0
- data/lib/rbitter/records_migrate/20150327_add_index.rb +12 -0
- data/lib/rbitter/records_migrate/20150504_add_replyto_column.rb +12 -0
- data/lib/rbitter/streaming.rb +105 -0
- data/lib/rbitter/version.rb +20 -0
- data/lib/rbitter/xmlrpc.rb +4 -0
- data/lib/rbitter/xmlrpcd/base.rb +25 -0
- data/lib/rbitter/xmlrpcd/rpchandles.rb +12 -0
- data/lib/rbitter/xmlrpcd/xmlrpc_auth_server.rb +83 -0
- data/lib/rbitter/xmlrpcd/xmlrpcd.rb +69 -0
- data/rbitter.gemspec +46 -0
- data/spec/config/.keep +0 -0
- data/spec/config/default.json +33 -0
- data/spec/rbitter/arcserver_spec.rb +30 -0
- data/spec/rbitter/console_spec.rb +9 -0
- data/spec/rbitter/default/config_json_spec.rb +3 -0
- data/spec/rbitter/dlthread_spec.rb +8 -0
- data/spec/rbitter/env_spec.rb +62 -0
- data/spec/rbitter/libtwitter_connection_override_spec.rb +8 -0
- data/spec/rbitter/records_spec.rb +13 -0
- data/spec/rbitter/streaming_spec.rb +9 -0
- data/spec/rbitter/version_spec.rb +8 -0
- data/spec/rbitter/xmlrpc_spec.rb +8 -0
- data/spec/rbitter/xmlrpcd/base_spec.rb +29 -0
- data/spec/rbitter/xmlrpcd/rpchandles_spec.rb +10 -0
- data/spec/rbitter/xmlrpcd/xmlrpc_auth_server_spec.rb +8 -0
- data/spec/rbitter/xmlrpcd/xmlrpcd_spec.rb +9 -0
- data/spec/rbitter_spec.rb +42 -0
- data/spec/sample_data/.keep +0 -0
- data/spec/spec_helper.rb +39 -0
- metadata +265 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rbitter
|
2
|
+
PRODUCT_NAME = "Rbitter"
|
3
|
+
VERSION = "0.1.2"
|
4
|
+
|
5
|
+
def major
|
6
|
+
VERSION.match(/^([0-9]+)\./)[1]
|
7
|
+
end
|
8
|
+
|
9
|
+
def minor
|
10
|
+
VERSION.match(/\.([0-9]+)\./)[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def patchlv
|
14
|
+
VERSION.match(/\.([0-9]+)$/)[1]
|
15
|
+
end
|
16
|
+
|
17
|
+
def version_string
|
18
|
+
"#{PRODUCT_NAME} #{VERSION}"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RPCHandles
|
4
|
+
RH_INFO = Struct.new("RPCHANDLE_INFO", :name, :version, :author, :description) {
|
5
|
+
def digest
|
6
|
+
"<rpchandle: #{name}-#{version} (written by #{author}, #{description})>"
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
module BaseHandle
|
11
|
+
# If a handler doesn't require an authorization, please inherit below class
|
12
|
+
class NoAuth < Object
|
13
|
+
def self.auth?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# If a handler does require an authorization, please inherit below class
|
19
|
+
class Auth < Object
|
20
|
+
def self.auth?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rbitter/xmlrpcd/rpchandles"
|
4
|
+
require "rbitter/xmlrpcd/base"
|
5
|
+
require "xmlrpc/server"
|
6
|
+
require "webrick"
|
7
|
+
|
8
|
+
module XMLRPC
|
9
|
+
class HTTPAuthXMLRPCServer < XMLRPC::WEBrickServlet
|
10
|
+
def extract_method(methodname, *args)
|
11
|
+
for name, obj in @handler
|
12
|
+
if obj.kind_of? Proc
|
13
|
+
next unless methodname == name
|
14
|
+
else
|
15
|
+
next unless methodname =~ /^#{name}(.+)$/
|
16
|
+
next unless obj.respond_to? $1
|
17
|
+
return obj.method($1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def service(request, response)
|
24
|
+
# Taken from xmlrpc/server.rb
|
25
|
+
if @valid_ip
|
26
|
+
raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip }
|
27
|
+
end
|
28
|
+
|
29
|
+
if request.request_method != "POST"
|
30
|
+
raise WEBrick::HTTPStatus::MethodNotAllowed,
|
31
|
+
"unsupported method `#{request.request_method}'."
|
32
|
+
end
|
33
|
+
|
34
|
+
if parse_content_type(request['Content-type']).first != "text/xml"
|
35
|
+
raise WEBrick::HTTPStatus::BadRequest
|
36
|
+
end
|
37
|
+
|
38
|
+
length = (request['Content-length'] || 0).to_i
|
39
|
+
|
40
|
+
raise WEBrick::HTTPStatus::LengthRequired unless length > 0
|
41
|
+
|
42
|
+
data = request.body
|
43
|
+
|
44
|
+
if data.nil? or data.bytesize != length
|
45
|
+
raise WEBrick::HTTPStatus::BadRequest
|
46
|
+
end
|
47
|
+
|
48
|
+
# Originally, process(data) was here.
|
49
|
+
# We need to check whether a method requires authorization.
|
50
|
+
rpc_method_name, rpc_params = parser().parseMethodCall(data)
|
51
|
+
rpc_method = extract_method(rpc_method_name)
|
52
|
+
|
53
|
+
if RPCHandles.auth.nil?
|
54
|
+
resp = handle(rpc_method_name, *rpc_params)
|
55
|
+
else
|
56
|
+
if rpc_method.owner.ancestors.include?(RPCHandles::BaseHandle::Auth)
|
57
|
+
# Check cookie and check it's valid
|
58
|
+
if request.cookies.size == 1 \
|
59
|
+
and request.cookies[0].name == "auth_key" \
|
60
|
+
and RPCHandles.auth.include?(request.cookies[0].value)
|
61
|
+
resp = handle(rpc_method_name, *rpc_params)
|
62
|
+
else
|
63
|
+
# Permission required
|
64
|
+
raise WEBrick::HTTPStatus::Forbidden
|
65
|
+
end
|
66
|
+
elsif rpc_method.owner.ancestors.include?(RPCHandles::BaseHandle::NoAuth)
|
67
|
+
resp = handle(rpc_method_name, *rpc_params)
|
68
|
+
else
|
69
|
+
raise WEBrick::HTTPStatus::Forbidden
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if resp.nil? or resp.bytesize <= 0
|
74
|
+
raise WEBrick::HTTPStatus::InternalServerError
|
75
|
+
end
|
76
|
+
|
77
|
+
response.status = 200
|
78
|
+
response['Content-Length'] = resp.bytesize
|
79
|
+
response['Content-Type'] = "text/xml; charset=utf-8"
|
80
|
+
response.body = resp
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rbitter/xmlrpcd/rpchandles"
|
4
|
+
require "rbitter/xmlrpcd/xmlrpc_auth_server"
|
5
|
+
require "webrick"
|
6
|
+
|
7
|
+
module Rbitter
|
8
|
+
RPC_PREFIX="rbitter"
|
9
|
+
|
10
|
+
class RPCServer
|
11
|
+
def initialize bind_host, bind_port
|
12
|
+
@server = WEBrick::HTTPServer.new(:Port => bind_port.to_i, :BindAddress => bind_host.to_s, :MaxClients => 4, :Logger => WEBrick::Log.new($stdout))
|
13
|
+
@core = XMLRPC::HTTPAuthXMLRPCServer.new
|
14
|
+
@core.set_default_handler { |name, *args|
|
15
|
+
"NO_COMMAND: #{name} with args #{args.inspect}"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_all_handles
|
20
|
+
Rbitter["xmlrpc"]["handles"].each { |path|
|
21
|
+
puts "[xmlrpc] Scanning handles from (#{path})"
|
22
|
+
Dir.entries(path).each { |fname|
|
23
|
+
fname = File.join(path, fname)
|
24
|
+
if File.exist?(fname) and File.file?(fname)
|
25
|
+
if fname.match(/rh_\w+\.rb$/)
|
26
|
+
begin
|
27
|
+
load fname
|
28
|
+
rescue Exception => e
|
29
|
+
# stub
|
30
|
+
puts "Exception while loading #{fname}"
|
31
|
+
puts e.inspect
|
32
|
+
end
|
33
|
+
else
|
34
|
+
puts "Ignored: #{fname}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
puts "[xmlrpc] found #{RPCHandles.constants.length} constants."
|
41
|
+
RPCHandles.constants.each { |handler|
|
42
|
+
if RPCHandles.const_get(handler).is_a?(Class)
|
43
|
+
@core.add_handler(RPC_PREFIX, RPCHandles.const_get(handler).new)
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def main_loop
|
49
|
+
load_all_handles
|
50
|
+
|
51
|
+
@server.mount("/", @core)
|
52
|
+
@server.start
|
53
|
+
|
54
|
+
puts "[xmlrpc] XMLRPC started"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class DummyRPCServer
|
59
|
+
def initialize bind_host, bind_port; end
|
60
|
+
|
61
|
+
def load_all_handles; end
|
62
|
+
|
63
|
+
def main_loop
|
64
|
+
puts "[xmlrpc] DummyRPCServer started"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class NullRPCServer; end
|
69
|
+
end
|
data/rbitter.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'rbitter/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "rbitter"
|
9
|
+
spec.version = Rbitter::VERSION
|
10
|
+
spec.authors = ["Nidev Plontra"]
|
11
|
+
spec.email = ["nidev.plontra@gmail.com"]
|
12
|
+
spec.summary = %q{Rbitter is a Twitter client specialized in archiving}
|
13
|
+
spec.description = %q{Rbitter archives all tweets appeared on user streaming using ActiveRecord. XMLRPC is used to serve archived tweets and useful features}
|
14
|
+
spec.homepage = "https://github.com/nidev/rbitter"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
|
23
|
+
spec.add_dependency 'twitter', '~> 5.14'
|
24
|
+
spec.add_dependency 'json', '~> 1.7'
|
25
|
+
spec.add_dependency 'ripl', '~> 0.7'
|
26
|
+
spec.add_dependency 'activerecord', '~> 4.0'
|
27
|
+
|
28
|
+
if RUBY_PLATFORM == 'java'
|
29
|
+
spec.platform = 'java'
|
30
|
+
|
31
|
+
spec.add_dependency 'activerecord-jdbc-adapter', '~> 1.3'
|
32
|
+
spec.add_dependency 'jdbc-sqlite3', '~> 3.8'
|
33
|
+
spec.add_dependency 'jdbc-mysql', '~> 5.1'
|
34
|
+
spec.add_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
|
35
|
+
spec.add_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3'
|
36
|
+
else
|
37
|
+
spec.platform = 'ruby'
|
38
|
+
|
39
|
+
spec.add_dependency 'sqlite3', '~> 1.3'
|
40
|
+
spec.add_dependency 'mysql2', '~> 0.3'
|
41
|
+
spec.add_dependency 'activerecord-mysql2-adapter', '~> 0.0.3'
|
42
|
+
end
|
43
|
+
|
44
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
45
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
46
|
+
end
|
data/spec/config/.keep
ADDED
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"twitter": {
|
3
|
+
"consumer_key": "",
|
4
|
+
"consumer_secret": "",
|
5
|
+
"access_token": "",
|
6
|
+
"access_token_secret": ""
|
7
|
+
},
|
8
|
+
"activerecord": "sqlite3",
|
9
|
+
"sqlite3": {
|
10
|
+
"dbfile": "rbitter.sqlite"
|
11
|
+
},
|
12
|
+
"mysql2": {
|
13
|
+
"host": "localhost",
|
14
|
+
"port": 3306,
|
15
|
+
"dbname": "archive",
|
16
|
+
"username": "",
|
17
|
+
"password": ""
|
18
|
+
},
|
19
|
+
"media_downloader": {
|
20
|
+
"cacert_path": "/cacerts/cacert.pem",
|
21
|
+
"download_dir": "imgs/"
|
22
|
+
},
|
23
|
+
"xmlrpc": {
|
24
|
+
"enable": true,
|
25
|
+
"bind_host": "0.0.0.0",
|
26
|
+
"bind_port": 1400,
|
27
|
+
"auth": {
|
28
|
+
"username": "username",
|
29
|
+
"password": "password"
|
30
|
+
},
|
31
|
+
"handles": ["/path/to/handles"]
|
32
|
+
}
|
33
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rbitter/arcserver"
|
4
|
+
require "rbitter/streaming"
|
5
|
+
require "rbitter/xmlrpcd/xmlrpcd"
|
6
|
+
|
7
|
+
describe Rbitter::ArcServer do
|
8
|
+
it 'is presented' do
|
9
|
+
expect(Rbitter::ArcServer).to be_a(Class)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'With dummy implementations, ' do
|
13
|
+
before(:all) do
|
14
|
+
Rbitter.bootstrap(['configure'])
|
15
|
+
expect(File.file?('config.json')).to be(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'successfully returns from main_loop' do
|
19
|
+
Rbitter.config_initialize
|
20
|
+
|
21
|
+
arcserver = Rbitter::ArcServer.new(Rbitter::DummyRPCServer)
|
22
|
+
arcserver.main_loop(Rbitter::DummyStreamClient)
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:all) do
|
26
|
+
File.delete('config.json') if File.exist?('config.json')
|
27
|
+
File.delete('rbitter.sqlite') if File.exist?('rbitter.sqlite')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rbitter"
|
4
|
+
|
5
|
+
describe Rbitter do
|
6
|
+
it 'has env_reset function and clears Rbitter.env,' do
|
7
|
+
Rbitter.env_reset
|
8
|
+
expect(Rbitter.env).to be_a(Hash)
|
9
|
+
expect(Rbitter.env.length).to be(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when config.json is not installed,' do
|
13
|
+
it 'fails on loading' do
|
14
|
+
expect{Rbitter.config_initialize}.to raise_error(Rbitter::ConfigFileError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when path to config.json is invalid,' do
|
19
|
+
it 'fals on loading' do
|
20
|
+
expect{Rbitter.config_initialize("/silly/dummy/.")}.to raise_error(Rbitter::ConfigFileError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
context 'when default config.json is installed,' do
|
26
|
+
before(:all) do
|
27
|
+
Rbitter.bootstrap(['configure'])
|
28
|
+
expect(File.file?('config.json')).to be(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'loads configuration successfully with autodetection' do
|
32
|
+
expect{Rbitter.config_initialize}.to_not raise_error
|
33
|
+
expect(Rbitter.env.length > 0).to be(true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'loads configuration successfully with given config.json path' do
|
37
|
+
expect{Rbitter.config_initialize('config.json')}.to_not raise_error
|
38
|
+
expect(Rbitter.env.length > 0).to be(true)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'checks that Rbitter.env returns Hash' do
|
42
|
+
expect{Rbitter.config_initialize}.to_not raise_error
|
43
|
+
expect(Rbitter.env).to be_a(Hash)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'checks that Rbitter[] works' do
|
47
|
+
expect{Rbitter.config_initialize}.to_not raise_error
|
48
|
+
expect(Rbitter["twitter"]).to be_a(Hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
after(:all) do
|
52
|
+
File.delete('config.json')
|
53
|
+
end
|
54
|
+
end
|
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
|
60
|
+
|
61
|
+
# TODO: Perform test with spec/config/default.json
|
62
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "rbitter/records"
|
3
|
+
|
4
|
+
describe Rbitter::Record do
|
5
|
+
# TODO: Perform test...
|
6
|
+
it 'has ActiveRecord class named Record' do
|
7
|
+
expect(Rbitter::Record).to be_a(Class)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has supportive module of ActiveRecord' do
|
11
|
+
expect(ARSupport).to be_a(Module)
|
12
|
+
end
|
13
|
+
end
|