atig 0.0.1
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.
- data/.gitignore +24 -0
- data/Gemfile +3 -0
- data/README.mkdn +52 -0
- data/Rakefile +15 -0
- data/atig.gemspec +25 -0
- data/bin/atig +74 -0
- data/docs/OMakefile +32 -0
- data/docs/OMakeroot +45 -0
- data/docs/_static/allow.png +0 -0
- data/docs/_static/emacs.png +0 -0
- data/docs/_static/irc_setting.png +0 -0
- data/docs/_static/irssi.png +0 -0
- data/docs/_static/limechat.png +0 -0
- data/docs/_static/limechat_s.png +0 -0
- data/docs/_static/oauth_channel.png +0 -0
- data/docs/_static/screenshot.png +0 -0
- data/docs/_static/structure.png +0 -0
- data/docs/_static/verify.png +0 -0
- data/docs/changelog.rst +96 -0
- data/docs/commandline_options.rst +21 -0
- data/docs/commands.rst +84 -0
- data/docs/conf.py +194 -0
- data/docs/config.rst +159 -0
- data/docs/feature.rst +41 -0
- data/docs/graphics.graffle +1995 -0
- data/docs/hacking_guide.rst +43 -0
- data/docs/index.rst +109 -0
- data/docs/irc.rst +31 -0
- data/docs/options.rst +75 -0
- data/docs/quickstart.rst +89 -0
- data/docs/resize.sh +7 -0
- data/docs/tiarra.rst +2 -0
- data/docs/tig.rst +21 -0
- data/lib/atig.rb +19 -0
- data/lib/atig/agent.rb +8 -0
- data/lib/atig/agent/agent.rb +38 -0
- data/lib/atig/agent/clenup.rb +23 -0
- data/lib/atig/agent/dm.rb +35 -0
- data/lib/atig/agent/following.rb +45 -0
- data/lib/atig/agent/full_list.rb +20 -0
- data/lib/atig/agent/list.rb +55 -0
- data/lib/atig/agent/list_status.rb +46 -0
- data/lib/atig/agent/mention.rb +13 -0
- data/lib/atig/agent/other_list.rb +18 -0
- data/lib/atig/agent/own_list.rb +18 -0
- data/lib/atig/agent/stream_follow.rb +38 -0
- data/lib/atig/agent/timeline.rb +13 -0
- data/lib/atig/agent/user_stream.rb +31 -0
- data/lib/atig/basic_twitter.rb +116 -0
- data/lib/atig/bitly.rb +52 -0
- data/lib/atig/channel.rb +5 -0
- data/lib/atig/channel/channel.rb +17 -0
- data/lib/atig/channel/dm.rb +14 -0
- data/lib/atig/channel/list.rb +76 -0
- data/lib/atig/channel/mention.rb +20 -0
- data/lib/atig/channel/retweet.rb +28 -0
- data/lib/atig/channel/timeline.rb +74 -0
- data/lib/atig/command.rb +21 -0
- data/lib/atig/command/autofix.rb +58 -0
- data/lib/atig/command/command.rb +24 -0
- data/lib/atig/command/command_helper.rb +95 -0
- data/lib/atig/command/destroy.rb +44 -0
- data/lib/atig/command/dm.rb +31 -0
- data/lib/atig/command/favorite.rb +27 -0
- data/lib/atig/command/info.rb +50 -0
- data/lib/atig/command/limit.rb +15 -0
- data/lib/atig/command/location.rb +23 -0
- data/lib/atig/command/name.rb +18 -0
- data/lib/atig/command/option.rb +37 -0
- data/lib/atig/command/refresh.rb +18 -0
- data/lib/atig/command/reply.rb +37 -0
- data/lib/atig/command/retweet.rb +63 -0
- data/lib/atig/command/search.rb +51 -0
- data/lib/atig/command/spam.rb +26 -0
- data/lib/atig/command/status.rb +41 -0
- data/lib/atig/command/thread.rb +44 -0
- data/lib/atig/command/time.rb +32 -0
- data/lib/atig/command/uptime.rb +32 -0
- data/lib/atig/command/user.rb +42 -0
- data/lib/atig/command/user_info.rb +27 -0
- data/lib/atig/command/version.rb +49 -0
- data/lib/atig/command/whois.rb +39 -0
- data/lib/atig/db/db.rb +60 -0
- data/lib/atig/db/followings.rb +131 -0
- data/lib/atig/db/listenable.rb +22 -0
- data/lib/atig/db/lists.rb +76 -0
- data/lib/atig/db/roman.rb +30 -0
- data/lib/atig/db/sized_uniq_array.rb +62 -0
- data/lib/atig/db/sql.rb +35 -0
- data/lib/atig/db/statuses.rb +147 -0
- data/lib/atig/db/transaction.rb +47 -0
- data/lib/atig/exception_util.rb +26 -0
- data/lib/atig/gateway.rb +62 -0
- data/lib/atig/gateway/channel.rb +99 -0
- data/lib/atig/gateway/session.rb +326 -0
- data/lib/atig/http.rb +95 -0
- data/lib/atig/ifilter.rb +7 -0
- data/lib/atig/ifilter/expand_url.rb +74 -0
- data/lib/atig/ifilter/retweet.rb +14 -0
- data/lib/atig/ifilter/retweet_time.rb +16 -0
- data/lib/atig/ifilter/sanitize.rb +18 -0
- data/lib/atig/ifilter/strip.rb +15 -0
- data/lib/atig/ifilter/utf7.rb +26 -0
- data/lib/atig/ifilter/xid.rb +36 -0
- data/lib/atig/levenshtein.rb +49 -0
- data/lib/atig/monkey.rb +4 -0
- data/lib/atig/oauth-patch.rb +40 -0
- data/lib/atig/oauth.rb +55 -0
- data/lib/atig/ofilter.rb +4 -0
- data/lib/atig/ofilter/escape_url.rb +102 -0
- data/lib/atig/ofilter/footer.rb +20 -0
- data/lib/atig/ofilter/geo.rb +17 -0
- data/lib/atig/ofilter/short_url.rb +47 -0
- data/lib/atig/option.rb +90 -0
- data/lib/atig/scheduler.rb +79 -0
- data/lib/atig/search.rb +22 -0
- data/lib/atig/search_twitter.rb +21 -0
- data/lib/atig/sized_hash.rb +33 -0
- data/lib/atig/stream.rb +66 -0
- data/lib/atig/twitter.rb +79 -0
- data/lib/atig/twitter_struct.rb +63 -0
- data/lib/atig/unu.rb +27 -0
- data/lib/atig/update_checker.rb +53 -0
- data/lib/atig/url_escape.rb +62 -0
- data/lib/atig/util.rb +16 -0
- data/lib/atig/version.rb +3 -0
- data/lib/memory_profiler.rb +77 -0
- data/spec/command/autofix_spec.rb +35 -0
- data/spec/command/destroy_spec.rb +98 -0
- data/spec/command/dm_spec.rb +28 -0
- data/spec/command/favorite_spec.rb +55 -0
- data/spec/command/limit_spec.rb +27 -0
- data/spec/command/location_spec.rb +25 -0
- data/spec/command/name_spec.rb +19 -0
- data/spec/command/option_spec.rb +133 -0
- data/spec/command/refresh_spec.rb +22 -0
- data/spec/command/reply_spec.rb +79 -0
- data/spec/command/retweet_spec.rb +66 -0
- data/spec/command/spam_spec.rb +27 -0
- data/spec/command/status_spec.rb +44 -0
- data/spec/command/thread_spec.rb +91 -0
- data/spec/command/time_spec.rb +52 -0
- data/spec/command/uptime_spec.rb +55 -0
- data/spec/command/user_info_spec.rb +42 -0
- data/spec/command/user_spec.rb +50 -0
- data/spec/command/version_spec.rb +67 -0
- data/spec/command/whois_spec.rb +78 -0
- data/spec/db/followings_spec.rb +100 -0
- data/spec/db/listenable_spec.rb +32 -0
- data/spec/db/lists_spec.rb +104 -0
- data/spec/db/roman_spec.rb +17 -0
- data/spec/db/sized_uniq_array_spec.rb +63 -0
- data/spec/db/statuses_spec.rb +180 -0
- data/spec/ifilter/expand_url_spec.rb +44 -0
- data/spec/ifilter/retweet_spec.rb +28 -0
- data/spec/ifilter/retweet_time_spec.rb +25 -0
- data/spec/ifilter/sanitize_spec.rb +25 -0
- data/spec/ifilter/sid_spec.rb +29 -0
- data/spec/ifilter/strip_spec.rb +23 -0
- data/spec/ifilter/tid_spec.rb +29 -0
- data/spec/ifilter/utf7_spec.rb +30 -0
- data/spec/levenshtein_spec.rb +24 -0
- data/spec/ofilter/escape_url_spec.rb +50 -0
- data/spec/ofilter/footer_spec.rb +32 -0
- data/spec/ofilter/geo_spec.rb +33 -0
- data/spec/ofilter/short_url_spec.rb +127 -0
- data/spec/option_spec.rb +91 -0
- data/spec/sized_hash_spec.rb +45 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/update_checker_spec.rb +55 -0
- metadata +326 -0
data/lib/atig/stream.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'logger'
|
|
6
|
+
require 'atig/twitter_struct'
|
|
7
|
+
require 'atig/util'
|
|
8
|
+
require 'atig/url_escape'
|
|
9
|
+
|
|
10
|
+
module Atig
|
|
11
|
+
class Stream
|
|
12
|
+
include Util
|
|
13
|
+
|
|
14
|
+
class APIFailed < StandardError; end
|
|
15
|
+
def initialize(context, user, password)
|
|
16
|
+
@log = context.log
|
|
17
|
+
@opts = context.opts
|
|
18
|
+
@user = user
|
|
19
|
+
@password = password
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def watch(path, query={}, &f)
|
|
23
|
+
path.sub!(%r{\A/+}, "")
|
|
24
|
+
|
|
25
|
+
uri = api_base
|
|
26
|
+
uri.path += path
|
|
27
|
+
uri.path += ".json"
|
|
28
|
+
uri.query = query.to_query_str unless query.empty?
|
|
29
|
+
|
|
30
|
+
@log.debug [uri.to_s]
|
|
31
|
+
|
|
32
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
33
|
+
request = Net::HTTP::Post.new uri.request_uri
|
|
34
|
+
request.basic_auth @user, @password
|
|
35
|
+
|
|
36
|
+
http.request(request) do |response|
|
|
37
|
+
unless response.code == '200' then
|
|
38
|
+
raise APIFailed,"#{response.code} #{response.message}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
buffer = ''
|
|
43
|
+
response.read_body do |chunk|
|
|
44
|
+
next if chunk.chomp.empty?
|
|
45
|
+
buffer << chunk.to_s
|
|
46
|
+
|
|
47
|
+
if buffer =~ /\A(.*)\n/ then
|
|
48
|
+
text = $1
|
|
49
|
+
unless text.strip.empty?
|
|
50
|
+
f.call TwitterStruct.make(JSON.parse(text))
|
|
51
|
+
end
|
|
52
|
+
buffer = ''
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
rescue => e
|
|
56
|
+
raise APIFailed,e.to_s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def api_base
|
|
63
|
+
URI(@opts.stream_api_base)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/atig/twitter.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
require 'atig/basic_twitter'
|
|
4
|
+
require 'atig/http'
|
|
5
|
+
|
|
6
|
+
module Atig
|
|
7
|
+
# from tig.rb
|
|
8
|
+
class Twitter < BasicTwitter
|
|
9
|
+
def initialize(context, oauth)
|
|
10
|
+
super context, :api_base
|
|
11
|
+
@oauth = oauth
|
|
12
|
+
@http = Atig::Http.new @log
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# authenticate = trueでないとSSL verified errorがでることがある
|
|
16
|
+
def page(path, name, authenticate = true, &block)
|
|
17
|
+
limit = 0.98 * @remain # 98% of IP based rate limit
|
|
18
|
+
r = []
|
|
19
|
+
cursor = -1
|
|
20
|
+
1.upto(limit) do |num|
|
|
21
|
+
# next_cursor にアクセスするとNot found が返ってくることがあるので,その時はbreak
|
|
22
|
+
ret = api(path, { :cursor => cursor }, { :authenticate => authenticate }) rescue break
|
|
23
|
+
arr = ret[name.to_s]
|
|
24
|
+
r.concat arr
|
|
25
|
+
cursor = ret[:next_cursor]
|
|
26
|
+
break if cursor.zero?
|
|
27
|
+
end
|
|
28
|
+
r
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.http_methods(*methods)
|
|
32
|
+
methods.each do |m|
|
|
33
|
+
self.module_eval <<END
|
|
34
|
+
def #{m}(path, query = {}, opts = {})
|
|
35
|
+
opts.update( :method => :#{m})
|
|
36
|
+
api path, query, opts
|
|
37
|
+
end
|
|
38
|
+
END
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
http_methods :get, :post, :put, :delete
|
|
42
|
+
|
|
43
|
+
protected
|
|
44
|
+
def request(uri, opts)
|
|
45
|
+
authenticate = opts.fetch(:authenticate, true)
|
|
46
|
+
method = opts.fetch(:method, :get)
|
|
47
|
+
|
|
48
|
+
header = {}
|
|
49
|
+
req = @http.req method, uri, header
|
|
50
|
+
@log.debug [req.method, uri.to_s]
|
|
51
|
+
|
|
52
|
+
if authenticate
|
|
53
|
+
oauth 30, req
|
|
54
|
+
else
|
|
55
|
+
@http.http(uri, 30, 30).request req
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def oauth(time, req)
|
|
60
|
+
timeout(time) do
|
|
61
|
+
headers = {}
|
|
62
|
+
req.each{|k,v| headers[k] = v }
|
|
63
|
+
|
|
64
|
+
case req
|
|
65
|
+
when Net::HTTP::Get
|
|
66
|
+
@oauth.get req.path,headers
|
|
67
|
+
when Net::HTTP::Head
|
|
68
|
+
@oauth.head req.path,headers
|
|
69
|
+
when Net::HTTP::Post
|
|
70
|
+
@oauth.post req.path,req.body,headers
|
|
71
|
+
when Net::HTTP::Put
|
|
72
|
+
@oauth.put req.path,req.body,headers
|
|
73
|
+
when Net::HTTP::Delete
|
|
74
|
+
@oauth.delete req.path,headers
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
module Atig
|
|
5
|
+
# from tig.rb
|
|
6
|
+
class TwitterStruct
|
|
7
|
+
def self.make(obj)
|
|
8
|
+
case obj
|
|
9
|
+
when Hash
|
|
10
|
+
obj = obj.dup
|
|
11
|
+
obj.each do |k, v|
|
|
12
|
+
obj[k] = TwitterStruct.make(v)
|
|
13
|
+
end
|
|
14
|
+
TwitterStruct.new(obj)
|
|
15
|
+
when Array
|
|
16
|
+
obj.map {|i| TwitterStruct.make(i) }
|
|
17
|
+
else
|
|
18
|
+
obj
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(obj)
|
|
23
|
+
@obj = obj
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def id
|
|
27
|
+
@obj["id"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def [](name)
|
|
31
|
+
@obj[name.to_s]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def []=(name,val)
|
|
35
|
+
@obj[name.to_s] = val
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def merge(hash)
|
|
39
|
+
obj = @obj.dup
|
|
40
|
+
hash.each do|key,value|
|
|
41
|
+
obj[key.to_s] = value
|
|
42
|
+
end
|
|
43
|
+
TwitterStruct.make obj
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def hash
|
|
47
|
+
self.id ? self.id.hash : super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def eql?(other)
|
|
51
|
+
self.hash == other.hash
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ==(other)
|
|
55
|
+
self.hash == other.hash
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def method_missing(sym, *args)
|
|
59
|
+
# XXX
|
|
60
|
+
@obj[sym.to_s]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/atig/unu.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
module Atig
|
|
3
|
+
class Unu
|
|
4
|
+
def initialize(logger)
|
|
5
|
+
@log = logger
|
|
6
|
+
@http = Atig::Http.new logger
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def shorten(url)
|
|
10
|
+
unu_url = "http://u.nu/"
|
|
11
|
+
unu = URI("#{unu_url}unu-api-simple")
|
|
12
|
+
url = URI.rstrip url
|
|
13
|
+
unu.query = { :url => url }.to_query_str
|
|
14
|
+
res = @http.http(unu, 5, 5).request(@http.req(:get, unu)).body
|
|
15
|
+
|
|
16
|
+
if res[0, 12] == unu_url
|
|
17
|
+
res
|
|
18
|
+
else
|
|
19
|
+
@log.error res
|
|
20
|
+
url
|
|
21
|
+
end
|
|
22
|
+
rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
|
|
23
|
+
@log.error e
|
|
24
|
+
url
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Atig
|
|
4
|
+
module UpdateChecker
|
|
5
|
+
def commits
|
|
6
|
+
uri = URI("http://github.com/api/v1/json/mzp/atig/commits/master")
|
|
7
|
+
http = Atig::Http.new
|
|
8
|
+
res = http.http(uri).request http.req(:get, uri)
|
|
9
|
+
JSON.parse(res.body)['commits']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def server_version
|
|
13
|
+
@server_version ||= instance_eval {
|
|
14
|
+
head = `git rev-parse HEAD 2>/dev/null`.chomp
|
|
15
|
+
head.empty?? "unknown" : head
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def local_repos?(rev)
|
|
20
|
+
system("git show #{rev} > /dev/null 2>&1")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def git?
|
|
24
|
+
system('which git > /dev/null 2>&1')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def latest
|
|
28
|
+
unless git? then
|
|
29
|
+
[]
|
|
30
|
+
else
|
|
31
|
+
cs = commits
|
|
32
|
+
latest = cs.first['id'][/^[0-9a-z]{40}$/]
|
|
33
|
+
raise "github API changed?" unless latest
|
|
34
|
+
|
|
35
|
+
if local_repos?(latest) then
|
|
36
|
+
[]
|
|
37
|
+
else
|
|
38
|
+
current = cs.map {|i| i['id'] }.index(server_version)
|
|
39
|
+
if current then
|
|
40
|
+
cs[0...current]
|
|
41
|
+
else
|
|
42
|
+
cs
|
|
43
|
+
end.map {|i| i['message'] }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
rescue Errno::ECONNREFUSED, Timeout::Error => e
|
|
47
|
+
[]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module_function :latest, :commits, :server_version, :local_repos?, :git?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
|
|
3
|
+
class Hash
|
|
4
|
+
# { :f => "v" } #=> "f=v"
|
|
5
|
+
# { "f" => [1, 2] } #=> "f=1&f=2"
|
|
6
|
+
# { "f" => "" } #=> "f="
|
|
7
|
+
# { "f" => nil } #=> "f"
|
|
8
|
+
def to_query_str separator = "&"
|
|
9
|
+
inject([]) do |r, (k, v)|
|
|
10
|
+
k = URI.encode_component k.to_s
|
|
11
|
+
(v.is_a?(Array) ? v : [v]).each do |i|
|
|
12
|
+
if i.nil?
|
|
13
|
+
r << k
|
|
14
|
+
else
|
|
15
|
+
r << "#{k}=#{URI.encode_component i.to_s}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
r
|
|
19
|
+
end.join separator
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class String
|
|
24
|
+
def ch?
|
|
25
|
+
/\A[&#+!][^ \007,]{1,50}\z/ === self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def screen_name?
|
|
29
|
+
/\A[A-Za-z0-9_]{1,15}\z/ === self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def encoding! enc
|
|
33
|
+
return self unless respond_to? :force_encoding
|
|
34
|
+
force_encoding enc
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module URI::Escape
|
|
39
|
+
alias :_orig_escape :escape
|
|
40
|
+
|
|
41
|
+
if defined? ::RUBY_REVISION and RUBY_REVISION < 24544
|
|
42
|
+
# URI.escape("あ1") #=> "%E3%81%82\xEF\xBC\x91"
|
|
43
|
+
# URI("file:///4") #=> #<URI::Generic:0x9d09db0 URL:file:/4>
|
|
44
|
+
# "\\d" -> "[0-9]" for Ruby 1.9
|
|
45
|
+
def escape str, unsafe = %r{[^-_.!~*'()a-zA-Z0-9;/?:@&=+$,\[\]]} #'
|
|
46
|
+
_orig_escape(str, unsafe)
|
|
47
|
+
end
|
|
48
|
+
alias :encode :escape
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def encode_component str, unsafe = /[^-_.!~*'()a-zA-Z0-9 ]/
|
|
52
|
+
_orig_escape(str, unsafe).tr(" ", "+")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def rstrip str
|
|
56
|
+
str.sub(%r{
|
|
57
|
+
(?: ( / [^/?#()]* (?: \( [^/?#()]* \) [^/?#()]* )* ) \) [^/?#()]*
|
|
58
|
+
| \.
|
|
59
|
+
) \z
|
|
60
|
+
}x, "\\1")
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/atig/util.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'atig/exception_util'
|
|
4
|
+
module Atig
|
|
5
|
+
module Util
|
|
6
|
+
private
|
|
7
|
+
include ExceptionUtil
|
|
8
|
+
def log(type, s)
|
|
9
|
+
if @log then
|
|
10
|
+
@log.send type, "[#{self.class}] #{s}"
|
|
11
|
+
else
|
|
12
|
+
STDERR.puts s
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/atig/version.rb
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# This is a memory profiler for Ruby. Once started, it runs in a thread in the
|
|
2
|
+
# background, periodically inspecting Ruby's ObjectSpace to look for new
|
|
3
|
+
# objects and printing a count of objects added and removed since the previous
|
|
4
|
+
# cycle.
|
|
5
|
+
#
|
|
6
|
+
# To use the profiler, do something like this:
|
|
7
|
+
#
|
|
8
|
+
# require 'memory_profiler'
|
|
9
|
+
#
|
|
10
|
+
# MemoryProfiler.start
|
|
11
|
+
#
|
|
12
|
+
# The profiler will write logs to ./log/memory_profiler.log.
|
|
13
|
+
#
|
|
14
|
+
# If you start MemoryProfiler with the ':string_debug => true' option, then it
|
|
15
|
+
# will dump a list of all strings in the app into the log/ directory after
|
|
16
|
+
# each cycle. You can then use 'diff' to spot which strings were added
|
|
17
|
+
# between runs.
|
|
18
|
+
class MemoryProfiler
|
|
19
|
+
DEFAULTS = {:delay => 10, :string_debug => false}
|
|
20
|
+
|
|
21
|
+
def self.start(opt={})
|
|
22
|
+
opt = DEFAULTS.dup.merge(opt)
|
|
23
|
+
|
|
24
|
+
Thread.new do
|
|
25
|
+
prev = Hash.new(0)
|
|
26
|
+
curr = Hash.new(0)
|
|
27
|
+
curr_strings = []
|
|
28
|
+
delta = Hash.new(0)
|
|
29
|
+
|
|
30
|
+
file = File.open("log/memory_profiler.#{Time.now.to_i}.log",'w')
|
|
31
|
+
|
|
32
|
+
loop do
|
|
33
|
+
begin
|
|
34
|
+
GC.start
|
|
35
|
+
curr.clear
|
|
36
|
+
|
|
37
|
+
curr_strings = [] if opt[:string_debug]
|
|
38
|
+
|
|
39
|
+
ObjectSpace.each_object do |o|
|
|
40
|
+
curr[o.class] += 1 #Marshal.dump(o).size rescue 1
|
|
41
|
+
if opt[:string_debug] and o.class == String
|
|
42
|
+
curr_strings.push o
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if opt[:string_debug]
|
|
47
|
+
File.open("log/memory_profiler_strings.log.#{Time.now.to_i}",'w') do |f|
|
|
48
|
+
curr_strings.sort.each do |s|
|
|
49
|
+
f.puts s
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
curr_strings.clear
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
delta.clear
|
|
56
|
+
(curr.keys + delta.keys).uniq.each do |k,v|
|
|
57
|
+
delta[k] = curr[k]-prev[k]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
file.puts "Top 20: #{Time.now}"
|
|
61
|
+
delta.sort_by { |k,v| -v.abs }[0..19].sort_by { |k,v| -v}.each do |k,v|
|
|
62
|
+
file.printf "%+5d: %s (%d)\n", v, k.name, curr[k] unless v == 0
|
|
63
|
+
end
|
|
64
|
+
file.flush
|
|
65
|
+
|
|
66
|
+
delta.clear
|
|
67
|
+
prev.clear
|
|
68
|
+
prev.update curr
|
|
69
|
+
GC.start
|
|
70
|
+
rescue Exception => err
|
|
71
|
+
STDERR.puts "** memory_profiler error: #{err}"
|
|
72
|
+
end
|
|
73
|
+
sleep opt[:delay]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|