liquid-ext 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/Gemfile +7 -0
- data/LICENSE +23 -0
- data/README.md +33 -0
- data/Rakefile +3 -0
- data/lib/liquid/benchmark.rb +9 -0
- data/lib/liquid/boot.rb +86 -0
- data/lib/liquid/cli.rb +80 -0
- data/lib/liquid/configuration.rb +158 -0
- data/lib/liquid/environment.rb +67 -0
- data/lib/liquid/ext/array.rb +23 -0
- data/lib/liquid/ext/enumerable.rb +20 -0
- data/lib/liquid/ext/hash.rb +31 -0
- data/lib/liquid/ext/nil.rb +5 -0
- data/lib/liquid/ext/object_space.rb +49 -0
- data/lib/liquid/ext/ordered_set.rb +10 -0
- data/lib/liquid/ext/signal.rb +13 -0
- data/lib/liquid/ext/string.rb +3 -0
- data/lib/liquid/ext/thread.rb +17 -0
- data/lib/liquid/from_file.rb +25 -0
- data/lib/liquid/gc_stats.rb +68 -0
- data/lib/liquid/hash_helper.rb +10 -0
- data/lib/liquid/proc_stat.rb +56 -0
- data/lib/liquid/router.rb +46 -0
- data/lib/liquid/statistics.rb +11 -0
- data/lib/liquid/sysconf.rb +25 -0
- data/lib/liquid/tasks/protoc.rake +6 -0
- data/lib/liquid/tasks/reek.rake +7 -0
- data/lib/liquid/tasks/router.rake +17 -0
- data/lib/liquid/tasks/rspec.rake +17 -0
- data/lib/liquid/tasks/yard.rake +8 -0
- data/lib/liquid/tasks.rb +1 -0
- data/lib/liquid/transaction_id.rb +13 -0
- data/liquid-ext.gemspec +23 -0
- data/spec/lib/liquid/ext/enumerable_spec.rb +25 -0
- data/spec/lib/liquid/router_spec.rb +77 -0
- data/spec/spec_helper.rb +10 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ccafb95dc4aafbdfcc5c4c75916926fa9a332dcd
|
4
|
+
data.tar.gz: 6e9e8d993ae413ca7d0630ea68dc469061195c54
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 509061281f8ba50abbcf3463d0c946fbe3fdf1286a6520617fb074f6e9aea4eb1539c88973d1ae59f10f3d22859f34608d79e206d918c28cec43f69715ade016
|
7
|
+
data.tar.gz: 697911fda7ae6c9a56c1f50a56e535fbda9bea71702af6b3d036b3d338544b79e8f773c5e18a4e891d5320d446bce476e2321af7151b8117ca4498411ff2538a
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--protected --no-private
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012 madvertise Mobile Advertising GmbH
|
2
|
+
Copyright (c) 2013 LiquidM, Inc.
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Liquid Extensions
|
2
|
+
|
3
|
+
The liquid-ext gem provides a bunch of ruby extensions and common startup/runtime code.
|
4
|
+
|
5
|
+
[](http://travis-ci.org/liquidm/ext)
|
6
|
+
[](https://codeclimate.com/github/liquidm/ext)
|
7
|
+
[](https://gemnasium.com/liquidm/ext)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'liquid-ext'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install liquid-ext
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Please refer to the [API documentation](http://rubydoc.info/gems/liquid-ext/frames).
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
33
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/liquid/boot.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$:.unshift(ROOT) if defined?(ROOT)
|
4
|
+
|
5
|
+
# load default configuration
|
6
|
+
require 'liquid-logging'
|
7
|
+
require 'liquid/configuration'
|
8
|
+
|
9
|
+
$conf = Configuration.new
|
10
|
+
|
11
|
+
# configuration-reloading callbacks
|
12
|
+
reload_logger = ->(conf) do
|
13
|
+
ImprovedLogger::Formatter.format = conf.log_format
|
14
|
+
ImprovedLogger::Formatter.log4j_format = conf.log4j_format
|
15
|
+
|
16
|
+
$log = MultiLogger.new
|
17
|
+
$log.attach(ImprovedLogger.new(conf.log_backend.to_sym, File.basename($0)))
|
18
|
+
$log.level = conf.log_level.downcase.to_sym
|
19
|
+
$log.log_caller = conf.log_caller
|
20
|
+
|
21
|
+
# sneak this in automatically
|
22
|
+
ZK.logger = $log if ::Module.const_defined?(:ZK)
|
23
|
+
end
|
24
|
+
|
25
|
+
reload_mixins = ->(conf) do
|
26
|
+
if defined?(ROOT)
|
27
|
+
config_yml = File.join(ROOT, 'config.yml')
|
28
|
+
conf.mixin(config_yml) if File.exist?(config_yml)
|
29
|
+
|
30
|
+
dot_user = File.join(ROOT, '.user')
|
31
|
+
|
32
|
+
if File.exists?(dot_user)
|
33
|
+
File.readlines(dot_user).each do |line|
|
34
|
+
user_yml = File.join(ROOT, 'config', 'mixins', "#{line.chomp}.yml")
|
35
|
+
conf.mixin(user_yml)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# reload configuration, trigger callbacks
|
42
|
+
$conf.callback(&reload_mixins)
|
43
|
+
$conf.callback(&reload_logger)
|
44
|
+
|
45
|
+
$conf.reload!
|
46
|
+
|
47
|
+
# load java dependencies
|
48
|
+
if RUBY_PLATFORM == 'java'
|
49
|
+
begin
|
50
|
+
require 'jbundler'
|
51
|
+
rescue LoadError
|
52
|
+
# do nothing
|
53
|
+
end
|
54
|
+
|
55
|
+
# some java libraries cannot be found on maven central, so we load all bundled
|
56
|
+
# jar files here for convenience
|
57
|
+
if defined?(ROOT)
|
58
|
+
Dir[File.join(ROOT, 'jars', '*.jar')].each do |f|
|
59
|
+
require f
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# load a bunch of common classes here, so we don't have to track and repeat it
|
65
|
+
# everywhere
|
66
|
+
require 'active_support/all'
|
67
|
+
require 'cgi'
|
68
|
+
require 'date'
|
69
|
+
require 'json'
|
70
|
+
require 'socket'
|
71
|
+
require 'time'
|
72
|
+
|
73
|
+
# load all extensions
|
74
|
+
Dir[File.join(File.dirname(__FILE__), 'ext', '*.rb')].each do |f|
|
75
|
+
require f
|
76
|
+
end
|
77
|
+
|
78
|
+
if RUBY_PLATFORM == 'java'
|
79
|
+
require 'liquid/benchmark'
|
80
|
+
end
|
81
|
+
|
82
|
+
require 'liquid/cli'
|
83
|
+
require 'liquid/environment'
|
84
|
+
require 'liquid/from_file'
|
85
|
+
require 'liquid/hash_helper'
|
86
|
+
require 'liquid/transaction_id'
|
data/lib/liquid/cli.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'liquid/boot'
|
4
|
+
require 'mixlib/cli'
|
5
|
+
|
6
|
+
class CLI
|
7
|
+
include Mixlib::CLI
|
8
|
+
|
9
|
+
option :configfile,
|
10
|
+
short: '-c FILE',
|
11
|
+
long: '--config FILE',
|
12
|
+
description: 'Configuration File to load'
|
13
|
+
|
14
|
+
option :name,
|
15
|
+
:short => '-n NAME',
|
16
|
+
:long => '--name NAME',
|
17
|
+
:description => 'Process name',
|
18
|
+
:default => File.basename($0),
|
19
|
+
:proc => ->(value) { $0 = value }
|
20
|
+
|
21
|
+
option :environment,
|
22
|
+
:short => '-e ENVIRONMENT',
|
23
|
+
:long => '--environment ENVIRONMENT',
|
24
|
+
:description => "Set the daemon environment",
|
25
|
+
:default => "development",
|
26
|
+
:proc => ->(value) { Env.set(value) }
|
27
|
+
|
28
|
+
option :debug,
|
29
|
+
:short => '-D',
|
30
|
+
:long => '--debug',
|
31
|
+
:description => "Enable debug output",
|
32
|
+
:boolean => true,
|
33
|
+
:default => false,
|
34
|
+
:proc => ->(value) { $conf.mixin(log_level: value ? :debug : :info); value }
|
35
|
+
|
36
|
+
option :help,
|
37
|
+
:short => '-h',
|
38
|
+
:long => '--help',
|
39
|
+
:description => "Show this message",
|
40
|
+
:on => :tail,
|
41
|
+
:boolean => true,
|
42
|
+
:show_options => true,
|
43
|
+
:exit => 0
|
44
|
+
|
45
|
+
def option(name, args)
|
46
|
+
args[:on] ||= :on
|
47
|
+
args[:boolean] ||= false
|
48
|
+
args[:required] ||= false
|
49
|
+
args[:proc] ||= nil
|
50
|
+
args[:show_options] ||= false
|
51
|
+
args[:exit] ||= nil
|
52
|
+
|
53
|
+
if args.has_key?(:default)
|
54
|
+
config[name.to_sym] = args[:default]
|
55
|
+
end
|
56
|
+
|
57
|
+
options[name.to_sym] = args
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.for(cls, &block)
|
61
|
+
cli = new
|
62
|
+
cli.instance_eval(&block) if block_given?
|
63
|
+
cli.parse_options
|
64
|
+
|
65
|
+
$log.info("cli:initialize", cli.config)
|
66
|
+
$conf.reload!
|
67
|
+
|
68
|
+
# infer some variables
|
69
|
+
opts = cli.config.merge({
|
70
|
+
fqdn: Socket.gethostbyname(Socket.gethostname).first
|
71
|
+
})
|
72
|
+
|
73
|
+
# sneak in opts without subclassing
|
74
|
+
cls.allocate.tap do |obj|
|
75
|
+
obj.define_singleton_method(:opts) { opts }
|
76
|
+
obj.send(:initialize)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
require 'liquid/ext/hash'
|
7
|
+
require 'liquid/ext/ordered_set'
|
8
|
+
require 'liquid/environment'
|
9
|
+
|
10
|
+
##
|
11
|
+
# A {Configuration} consists of one or more Sections. A section is a hash-like
|
12
|
+
# object that responds to all keys in the hash as if they were methods:
|
13
|
+
#
|
14
|
+
# > s = Section.from_hash({:v1 => 2, :nested => {:v2 => 1}})
|
15
|
+
# > s.v1
|
16
|
+
# => 2
|
17
|
+
# > s.nested.v2
|
18
|
+
# => 1
|
19
|
+
#
|
20
|
+
class Section < Hash
|
21
|
+
|
22
|
+
class << self
|
23
|
+
|
24
|
+
# How to handle nil values in the configuration?
|
25
|
+
#
|
26
|
+
# Possible values are:
|
27
|
+
# - :nil, nil (return nil)
|
28
|
+
# - :raise (raise an exception)
|
29
|
+
# - :section (return a NilSection which can be chained)
|
30
|
+
#
|
31
|
+
attr_accessor :nil_action
|
32
|
+
|
33
|
+
# Create a new section from the given hash-like object.
|
34
|
+
#
|
35
|
+
# @param [Hash] hsh The hash to convert into a section.
|
36
|
+
# @return [Section] The new {Section} object.
|
37
|
+
def from_hash(hsh)
|
38
|
+
new.tap do |result|
|
39
|
+
hsh.each do |key, value|
|
40
|
+
result[key.to_sym] = from_value(value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Convert the given value into a Section, list of Sections or the pure
|
46
|
+
# value. Used to recursively build the Section hash.
|
47
|
+
#
|
48
|
+
# @private
|
49
|
+
def from_value(value)
|
50
|
+
case value
|
51
|
+
when Hash
|
52
|
+
from_hash(value)
|
53
|
+
when Array
|
54
|
+
value.map do |item|
|
55
|
+
from_value(item)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
def method_missing(name, *args)
|
66
|
+
if name.to_s =~ /(.*)=$/
|
67
|
+
self[$1.to_sym] = Section.from_value(args.first)
|
68
|
+
else
|
69
|
+
value = self[name]
|
70
|
+
self[name] = value.call if value.is_a?(Proc)
|
71
|
+
self[name]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# The Configuration class provides a simple interface to configuration stored
|
78
|
+
# inside of YAML files.
|
79
|
+
#
|
80
|
+
class Configuration < Section
|
81
|
+
|
82
|
+
DEFAULTS = {
|
83
|
+
generic: {
|
84
|
+
log_backend: :stdout,
|
85
|
+
log_caller: false,
|
86
|
+
log_level: :info,
|
87
|
+
log_format: "%{time} %{progname}(%{pid}) [%{severity}] %{msg}\n",
|
88
|
+
log4j_format: "%d %c(%t) [%p] %m%n",
|
89
|
+
},
|
90
|
+
production: {
|
91
|
+
log_format: "%{msg}\n",
|
92
|
+
log4j_format: "%m%n",
|
93
|
+
},
|
94
|
+
staging: {
|
95
|
+
log_format: "%{msg}\n",
|
96
|
+
log4j_format: "%m%n",
|
97
|
+
},
|
98
|
+
}
|
99
|
+
|
100
|
+
# Create a new {Configuration} object.
|
101
|
+
#
|
102
|
+
# @yield [config] The new configuration object.
|
103
|
+
def initialize
|
104
|
+
@mixins = OrderedSet.new
|
105
|
+
@callbacks = []
|
106
|
+
mixin(DEFAULTS)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Mixin a configuration snippet into the current section.
|
110
|
+
#
|
111
|
+
# @param [Hash, String] value A hash to merge into the current
|
112
|
+
# configuration. If a string is given a filename
|
113
|
+
# is assumed and the given file is expected to
|
114
|
+
# contain a YAML hash.
|
115
|
+
# @return [void]
|
116
|
+
def mixin(value)
|
117
|
+
@mixins << value
|
118
|
+
|
119
|
+
if value.is_a?(String)
|
120
|
+
value = YAML.load(File.read(value))
|
121
|
+
end
|
122
|
+
|
123
|
+
return unless value
|
124
|
+
|
125
|
+
value = Section.from_hash(value)
|
126
|
+
|
127
|
+
deep_merge!(value[:generic]) if value.has_key?(:generic)
|
128
|
+
|
129
|
+
if value.has_key?(Env.to_sym)
|
130
|
+
deep_merge!(value[Env.to_sym])
|
131
|
+
else
|
132
|
+
deep_merge!(value)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Reload all mixins.
|
137
|
+
#
|
138
|
+
# @return [void]
|
139
|
+
def reload!
|
140
|
+
clear
|
141
|
+
|
142
|
+
@mixins.each do |file|
|
143
|
+
mixin(file)
|
144
|
+
end
|
145
|
+
|
146
|
+
@callbacks.each do |callback|
|
147
|
+
callback.call(self)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Register a callback for config mixins.
|
152
|
+
#
|
153
|
+
# @return [void]
|
154
|
+
def callback(&block)
|
155
|
+
@callbacks << block
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
##
|
2
|
+
# A simple convenience class to support multiple environments in which a
|
3
|
+
# program can run (e.g. development, production, etc).
|
4
|
+
#
|
5
|
+
class Environment
|
6
|
+
attr_accessor :key
|
7
|
+
|
8
|
+
# Create a new Environment instance with the corresponding +key+ in the +ENV+
|
9
|
+
# hash.
|
10
|
+
#
|
11
|
+
# @param [String] key The key in +ENV+ to contain the current program
|
12
|
+
# environment.
|
13
|
+
#
|
14
|
+
def initialize(key = "RACK_ENV")
|
15
|
+
@key = key
|
16
|
+
end
|
17
|
+
|
18
|
+
# Retreive the current environment mode.
|
19
|
+
#
|
20
|
+
# @return [String] The current environment mode.
|
21
|
+
def mode
|
22
|
+
ENV[@key] || 'development'
|
23
|
+
rescue
|
24
|
+
'development'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve the current environment mode and convert it to a symbol.
|
28
|
+
#
|
29
|
+
# @return [Symbol] The current environment mode.
|
30
|
+
def to_sym
|
31
|
+
mode.to_sym
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return true if the current environment is +production+.
|
35
|
+
def production?
|
36
|
+
to_sym == :production
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :prod? :production?
|
40
|
+
|
41
|
+
# Return true if the current environment is +production+.
|
42
|
+
def staging?
|
43
|
+
to_sym == :staging
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return true if the current environment is +development+.
|
47
|
+
def development?
|
48
|
+
to_sym == :development
|
49
|
+
end
|
50
|
+
|
51
|
+
alias :dev? :development?
|
52
|
+
|
53
|
+
# Return true if the current environment is +test+.
|
54
|
+
def test?
|
55
|
+
to_sym == :test
|
56
|
+
end
|
57
|
+
|
58
|
+
# Set the environment mode.
|
59
|
+
#
|
60
|
+
# @param [String] The new environment mode.
|
61
|
+
def set(value)
|
62
|
+
ENV[@key] = value.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Global instance of {Environment}.
|
67
|
+
Env = Environment.new
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
def pick(n=1)
|
4
|
+
Array.new(n) { self[Kernel::rand(size)-1] }
|
5
|
+
end
|
6
|
+
|
7
|
+
def pick_one
|
8
|
+
self[Kernel::rand(size)-1]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h(&block)
|
12
|
+
Hash[*self.map { |v| [v, block.call(v)] }.flatten]
|
13
|
+
end
|
14
|
+
|
15
|
+
def / parts
|
16
|
+
inject([[]]) do |ary, x|
|
17
|
+
ary << [] if [*ary.last].nitems == length / parts
|
18
|
+
ary.last << x
|
19
|
+
ary
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support/core_ext/enumerable'
|
2
|
+
|
3
|
+
module Enumerable
|
4
|
+
def mean
|
5
|
+
sum.to_f / length
|
6
|
+
end
|
7
|
+
|
8
|
+
def variance
|
9
|
+
m = mean
|
10
|
+
reduce(0) {|accum, item| accum + (item - m) ** 2}.to_f / (length - 1)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stdev
|
14
|
+
Math.sqrt(variance)
|
15
|
+
end
|
16
|
+
|
17
|
+
def percentile(pc)
|
18
|
+
sort[(pc * length).ceil - 1]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
##
|
2
|
+
# Various Hash extensions.
|
3
|
+
#
|
4
|
+
class Hash
|
5
|
+
|
6
|
+
# Recursively merge +other_hash+ into +self+ and return the new hash.
|
7
|
+
def deep_merge(other_hash)
|
8
|
+
self.merge(other_hash) do |key, oldval, newval|
|
9
|
+
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
10
|
+
newval = newval.to_hash if newval.respond_to?(:to_hash)
|
11
|
+
oldval.is_a?(Hash) && newval.is_a?(Hash) ? oldval.deep_merge(newval) : newval
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Recursively merge and replace +other_hash+ into +self+.
|
16
|
+
def deep_merge!(other_hash)
|
17
|
+
replace(deep_merge(other_hash))
|
18
|
+
end
|
19
|
+
|
20
|
+
# accumulate existing keys from +other_hash+ into +self+.
|
21
|
+
def delta_merge!(other_hash)
|
22
|
+
other_hash.each do |k,v|
|
23
|
+
if self.has_key?(k)
|
24
|
+
self[k] += v
|
25
|
+
else
|
26
|
+
self[k] = v
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Memory
|
2
|
+
WORD_SIZE = 8
|
3
|
+
OBJ_SIZE = 40 # some are smaller
|
4
|
+
OBJ_OVERHEAD = WORD_SIZE + OBJ_SIZE
|
5
|
+
|
6
|
+
def self.size(obj)
|
7
|
+
return WORD_SIZE if obj.is_a?(Fixnum)
|
8
|
+
|
9
|
+
case obj
|
10
|
+
when String
|
11
|
+
obj.size
|
12
|
+
when Array
|
13
|
+
obj.size * WORD_SIZE
|
14
|
+
when Hash
|
15
|
+
obj.size * WORD_SIZE * 2
|
16
|
+
#when Enumerable
|
17
|
+
# result = 0
|
18
|
+
# obj.each do |ref|
|
19
|
+
# result += WORD_SIZE
|
20
|
+
# end
|
21
|
+
# result
|
22
|
+
else
|
23
|
+
0
|
24
|
+
end + OBJ_OVERHEAD
|
25
|
+
rescue => e
|
26
|
+
puts "failed to get object size for #{obj.inspect}: #{e}"
|
27
|
+
return OBJ_OVERHEAD
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ObjectSpace
|
32
|
+
def self.memory_stats(*args)
|
33
|
+
stats = {}
|
34
|
+
|
35
|
+
self.each_object do |obj|
|
36
|
+
stats[obj.class] ||= []
|
37
|
+
stats[obj.class] << Memory.size(obj)
|
38
|
+
end
|
39
|
+
|
40
|
+
stats.map do |cls, sizes|
|
41
|
+
cnt = sizes.length
|
42
|
+
sum = sizes.reduce(:+)
|
43
|
+
avg = sum / cnt
|
44
|
+
[cls, [cnt, avg, sum]]
|
45
|
+
end.sort_by do |cls, sizes|
|
46
|
+
sizes[2]
|
47
|
+
end.reverse
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Signal
|
2
|
+
def self.register_shutdown_handler(&block)
|
3
|
+
signals = %w(INT TERM)
|
4
|
+
|
5
|
+
# The signal QUIT is in use by the JVM itself
|
6
|
+
signals << 'QUIT' unless RUBY_PLATFORM == 'java'
|
7
|
+
|
8
|
+
signals.each do |sig|
|
9
|
+
old = trap(sig) {}
|
10
|
+
trap(sig) { block.call; old.call }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# make default what should be default
|
2
|
+
Thread.abort_on_exception = true
|
3
|
+
|
4
|
+
class Thread
|
5
|
+
|
6
|
+
def self.name
|
7
|
+
Thread.current[:name] || Java::JavaLang::Thread.currentThread.getName
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.name=(value)
|
11
|
+
Thread.current[:name] = value
|
12
|
+
if RUBY_PLATFORM == "java"
|
13
|
+
Java::JavaLang::Thread.currentThread.setName(value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|