liquid-ext 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/liquidm/ext.png)](http://travis-ci.org/liquidm/ext)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/liquidm/ext.png)](https://codeclimate.com/github/liquidm/ext)
|
7
|
+
[![Dependency Status](https://gemnasium.com/liquidm/ext.png)](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
|