buzzware-buzzcore 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +23 -0
- data/README.rdoc +46 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/buzzcore.gemspec +77 -0
- data/buzzcore.vpj +93 -0
- data/buzzcore.vpw +6 -0
- data/lib/buzzcore.rb +2 -0
- data/lib/buzzcore/cap_utils.rb +181 -0
- data/lib/buzzcore/config.rb +210 -0
- data/lib/buzzcore/database_utils.rb +86 -0
- data/lib/buzzcore/enum.rb +50 -0
- data/lib/buzzcore/extend_base_classes.rb +320 -0
- data/lib/buzzcore/ftp_extra.rb +188 -0
- data/lib/buzzcore/logging.rb +159 -0
- data/lib/buzzcore/misc_utils.rb +382 -0
- data/lib/buzzcore/require_paths.rb +28 -0
- data/lib/buzzcore/shell_extras.rb +80 -0
- data/lib/buzzcore/string_utils.rb +53 -0
- data/lib/buzzcore/text_doc.rb +70 -0
- data/lib/buzzcore/thread_utils.rb +709 -0
- data/lib/buzzcore/xml_utils.rb +184 -0
- data/test/buzzcore_test.rb +7 -0
- data/test/config_test.rb +201 -0
- data/test/credentials_test.rb +71 -0
- data/test/shell_test.rb +54 -0
- data/test/test_helper.rb +10 -0
- metadata +95 -0
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'buzzcore/xml_utils'
|
2
|
+
require 'buzzcore/extend_base_classes'
|
3
|
+
|
4
|
+
class ConfigClass < Hash
|
5
|
+
|
6
|
+
attr_reader :default_values
|
7
|
+
|
8
|
+
def initialize(aDefaultValues,aNewValues=nil,&aBlock)
|
9
|
+
@default_values = aDefaultValues.clone
|
10
|
+
reset()
|
11
|
+
if aNewValues
|
12
|
+
block_given? ? read(aNewValues,&aBlock) : read(aNewValues)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# aBlock allows values to be filtered based on key,default and new values
|
17
|
+
def read(aSource,&aBlock)
|
18
|
+
default_values.each do |k,v|
|
19
|
+
done = false
|
20
|
+
if block_given? && ((newv = yield(k,v,aSource && aSource[k])) != nil)
|
21
|
+
self[k] = newv
|
22
|
+
done = true
|
23
|
+
end
|
24
|
+
copy_item(aSource,k) if !done && aSource && !aSource[k].nil?
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# reset values back to defaults
|
30
|
+
def reset
|
31
|
+
self.clear
|
32
|
+
me = self
|
33
|
+
@default_values.each {|n,v| me[n] = v.is_a?(Class) ? nil : v}
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_int(aKey,aValue)
|
37
|
+
case aValue
|
38
|
+
when String then self[aKey] = aValue.to_integer(self[aKey]);
|
39
|
+
when Fixnum then self[aKey] = aValue;
|
40
|
+
when Float then self[aKey] = aValue.to_i;
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_float(aKey,aValue)
|
45
|
+
case aValue
|
46
|
+
when String then self[aKey] = aValue.to_float(self[aKey]);
|
47
|
+
when Fixnum then self[aKey] = aValue.to_f;
|
48
|
+
when Float then self[aKey] = aValue;
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_boolean(aKey,aValue)
|
53
|
+
case aValue
|
54
|
+
when TrueClass,FalseClass then self[aKey] = aValue;
|
55
|
+
when String then self[aKey] = (['1','yes','y','true','on'].include?(aValue.downcase))
|
56
|
+
else
|
57
|
+
set_boolean(aKey,aValue.to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_symbol(aKey,aValue)
|
62
|
+
case aValue
|
63
|
+
when String then self[aKey] = (aValue.to_sym rescue nil);
|
64
|
+
when Symbol then self[aKey] = aValue;
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def copy_item(aHash,aKey)
|
69
|
+
d = default_values[aKey]
|
70
|
+
d_class = (d.is_a?(Class) ? d : d.class)
|
71
|
+
cname = d_class.name.to_sym
|
72
|
+
case cname
|
73
|
+
when :NilClass then ;
|
74
|
+
when :String then self[aKey] = aHash[aKey].to_s unless aHash[aKey].nil?
|
75
|
+
when :Float then set_float(aKey,aHash[aKey]);
|
76
|
+
when :Fixnum then set_int(aKey,aHash[aKey]);
|
77
|
+
when :TrueClass, :FalseClass then set_boolean(aKey,aHash[aKey]);
|
78
|
+
when :Symbol then self[aKey] = (aHash[aKey].to_sym rescue nil)
|
79
|
+
else
|
80
|
+
raise StandardError.new('unsupported type')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def copy_strings(aHash,*aKeys)
|
85
|
+
aKeys.each do |k|
|
86
|
+
self[k] = aHash[k].to_s unless aHash[k].nil?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def copy_ints(*aDb)
|
91
|
+
aHash = aDb.shift
|
92
|
+
aKeys = aDb
|
93
|
+
aKeys.each do |k|
|
94
|
+
set_int(k,aHash[k])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def copy_floats(aHash,*aKeys)
|
99
|
+
aKeys.each do |k|
|
100
|
+
set_float(k,aHash[k])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def copy_booleans(aHash,*aKeys)
|
105
|
+
aKeys.each do |k|
|
106
|
+
set_boolean(k,aHash[k])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_hash
|
111
|
+
{}.merge(self)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
class ConfigXmlClass < ConfigClass
|
117
|
+
attr_accessor :xmlRoot
|
118
|
+
def initialize(aDefaultValues,aConfig)
|
119
|
+
return super(aDefaultValues,aConfig) unless aConfig.is_a?(REXML::Element)
|
120
|
+
@xmlRoot = aConfig.deep_clone
|
121
|
+
super(aDefaultValues,XmlUtils.read_simple_items(@xmlRoot,'SimpleItems').symbolize_keys)
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.from_file(aDefaultValues,aFile)
|
125
|
+
xml = XmlUtils.get_file_root(aFile)
|
126
|
+
return ConfigXmlClass.new(aDefaultValues,xml)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# credentials files look like :
|
131
|
+
#<?xml version="1.0" encoding="UTF-8"?>
|
132
|
+
#<Credentials>
|
133
|
+
# <SimpleItems namespace="global">
|
134
|
+
# <Item name=""></Item>
|
135
|
+
# <Item name=""></Item>
|
136
|
+
# <Item name=""></Item>
|
137
|
+
# </SimpleItems>
|
138
|
+
# <SimpleItems namespace="yore_test">
|
139
|
+
# <Item name=""></Item>
|
140
|
+
# <Item name=""></Item>
|
141
|
+
# <Item name=""></Item>
|
142
|
+
# </SimpleItems>
|
143
|
+
#</Credentials>
|
144
|
+
#
|
145
|
+
# global .credentials.xml file
|
146
|
+
# local .credentials.xml file
|
147
|
+
# cred = Credentials.new() # optionally specify filename or path or hash. if nil then use Dir.pwd
|
148
|
+
#
|
149
|
+
# def initialize(aSource)
|
150
|
+
# # load global namespace from ~/.credentials.xml
|
151
|
+
# # load global namespace from local .credentials.xml
|
152
|
+
# # load given namespace from ~/.credentials.xml
|
153
|
+
# # load given namespace from local .credentials.xml
|
154
|
+
# # merge all top to bottom
|
155
|
+
class Credentials < Hash
|
156
|
+
|
157
|
+
CRED_FILENAME = ".credentials.xml"
|
158
|
+
|
159
|
+
def find_file_upwards(aFilename,aStartPath=nil)
|
160
|
+
aStartPath ||= Dir.pwd
|
161
|
+
return nil if aFilename.nil? || aFilename.empty?
|
162
|
+
arrPath = aStartPath.split(File::SEPARATOR)
|
163
|
+
while arrPath.length > 0
|
164
|
+
path = File.join(arrPath.join(File::SEPARATOR),aFilename)
|
165
|
+
return path if File.exists?(path)
|
166
|
+
arrPath.pop
|
167
|
+
end
|
168
|
+
return nil
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_all_credentials(aXmlRoot)
|
172
|
+
return nil unless aXmlRoot
|
173
|
+
result = {}
|
174
|
+
REXML::XPath.each(aXmlRoot, '/Credentials/SimpleItems') do |si|
|
175
|
+
ns = si.attributes['Namespace']
|
176
|
+
values = XmlUtils.read_simple_items(si)
|
177
|
+
result[ns.to_sym] = values.symbolize_keys if ns && values
|
178
|
+
end
|
179
|
+
return result
|
180
|
+
end
|
181
|
+
|
182
|
+
#XmlUtils.read_simple_items(@xmlRoot,'/Yore/SimpleItems')
|
183
|
+
def get_user_credentials
|
184
|
+
return get_all_credentials(XmlUtils.get_file_root(File.join(HOME_PATH,CRED_FILENAME)))
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_local_credentials(aSource=nil)
|
188
|
+
aSource ||= Dir.pwd
|
189
|
+
# assume source is a directory path, but other types could be supported later
|
190
|
+
return nil unless file=find_file_upwards(CRED_FILENAME,aSource)
|
191
|
+
return get_all_credentials(XmlUtils.get_file_root(file))
|
192
|
+
end
|
193
|
+
|
194
|
+
def initialize(aNamespace=nil,aSource=nil)
|
195
|
+
#HOME_PATH can be preset by tests eg. ::Credentials.const_set('HOME_PATH',@user_dir)
|
196
|
+
Credentials.const_set("HOME_PATH", ENV['HOME']) unless Credentials.const_defined? "HOME_PATH"
|
197
|
+
arrCredentials = []
|
198
|
+
user_credentials = get_user_credentials()
|
199
|
+
local_credentials = get_local_credentials(aSource)
|
200
|
+
arrCredentials << user_credentials[:global] if user_credentials
|
201
|
+
arrCredentials << local_credentials[:global] if local_credentials
|
202
|
+
arrCredentials << user_credentials[aNamespace.to_sym] if aNamespace && user_credentials
|
203
|
+
arrCredentials << local_credentials[aNamespace.to_sym] if aNamespace && local_credentials
|
204
|
+
arrCredentials.compact!
|
205
|
+
arrCredentials.each do |c|
|
206
|
+
self.merge!(c)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'buzzcore/shell_extras'
|
2
|
+
|
3
|
+
module DatabaseUtils
|
4
|
+
def self.execute_sql_file(filename,aUser=nil,aPassword=nil)
|
5
|
+
conf = ActiveRecord::Base.configurations[RAILS_ENV]
|
6
|
+
pw = aPassword || conf['password'].to_s || ''
|
7
|
+
user = aUser || conf['username'].to_s || ''
|
8
|
+
cmd_line = "mysql -h #{conf['host']} -D #{conf['database']} #{user.empty? ? '' : '-u '+user} #{pw.empty? ? '' : '-p'+pw} <#{filename}"
|
9
|
+
if !system(cmd_line)
|
10
|
+
raise Exception, "Error executing "+cmd_line
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
## http://www.cyberciti.biz/faq/how-do-i-empty-mysql-database/
|
15
|
+
#
|
16
|
+
#
|
17
|
+
## drop all tables :
|
18
|
+
## mysqldump -uusername -ppassword -hhost \
|
19
|
+
##--add-drop-table --no-data database | grep ^DROP | \
|
20
|
+
##mysql -uusername -ppassword -hhost database
|
21
|
+
#
|
22
|
+
|
23
|
+
def self.database_exists(aDbDetails,aDatabase=nil)
|
24
|
+
aDbDetails[:database] = aDatabase if aDatabase
|
25
|
+
return false if !aDbDetails[:database]
|
26
|
+
response = POpen4::shell("mysql -u #{aDbDetails[:username]} -p#{aDbDetails[:password]} -e 'use #{aDbDetails[:database]}'") do |r|
|
27
|
+
if r[:stderr] && r[:stderr].index("ERROR 1049 ")==0 # Unknown database
|
28
|
+
r[:exitcode] = 0
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
return (response && response[:exitcode]==0)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.clear_database(aDbDetails)
|
36
|
+
response = POpen4::shell("mysqldump -u #{aDbDetails[:username]} -p#{aDbDetails[:password]} --add-drop-table --no-data #{aDbDetails[:database]} | grep ^DROP | mysql -u #{aDbDetails[:username]} -p#{aDbDetails[:password]} #{aDbDetails[:database]}")
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.create_database(aDbDetails,aDatabase=nil)
|
40
|
+
aDbDetails[:database] = aDatabase if aDatabase
|
41
|
+
return false if !aDbDetails[:database]
|
42
|
+
response = POpen4::shell("mysqladmin -u #{aDbDetails[:username]} -p#{aDbDetails[:password]} create #{aDbDetails[:database]}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.ensure_empty_database(aDbDetails,aDatabase=nil)
|
46
|
+
aDbDetails[:database] = aDatabase if aDatabase
|
47
|
+
if database_exists(aDbDetails)
|
48
|
+
clear_database(aDbDetails)
|
49
|
+
else
|
50
|
+
create_database(aDbDetails)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.load_database(aDbDetails,aSqlFile)
|
55
|
+
ensure_empty_database(aDbDetails)
|
56
|
+
response = POpen4::shell("mysql -u #{aDbDetails[:username]} -p#{aDbDetails[:password]} #{aDbDetails[:database]} < #{aSqlFile}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.save_database(aDbDetails,aSqlFile)
|
60
|
+
response = POpen4::shell("mysqldump --user=#{aDbDetails[:username]} --password=#{aDbDetails[:password]} --skip-extended-insert #{aDbDetails[:database]} > #{aSqlFile}")
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
## eg. rake metas:spree:data:load from=/tmp/spree_data.tgz to=mysql:fresco_server_d:root:password
|
65
|
+
#desc 'load spree data from a file'
|
66
|
+
#task :load do
|
67
|
+
# from = ENV['from']
|
68
|
+
# to=ENV['to']
|
69
|
+
# db_server,db,user,password = to.split(':')
|
70
|
+
# tmpdir = make_temp_dir('metas')
|
71
|
+
# cmd = "tar -xvzf #{from} -C #{tmpdir}"
|
72
|
+
# puts CapUtilsClass.shell(cmd)
|
73
|
+
#
|
74
|
+
# ensure_empty_database(db_server,db,user,password)
|
75
|
+
#
|
76
|
+
# puts CapUtilsClass.shell("mysql -u #{user} -p#{password} #{db} < #{File.join(tmpdir,'db/dumps/db.sql')}")
|
77
|
+
# FileUtils.mkdir_p('public/assets')
|
78
|
+
# puts CapUtilsClass.shell("cp -rf #{File.join(tmpdir,'public/assets/products')} public/assets/products")
|
79
|
+
#end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Kernel
|
2
|
+
# simple (sequential) enumerated values
|
3
|
+
# usage :
|
4
|
+
#
|
5
|
+
# module Constants
|
6
|
+
# module Gradient
|
7
|
+
# enum :B, :A, :C
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# then :
|
12
|
+
#
|
13
|
+
# puts Constants::Gradient::B -> 0
|
14
|
+
# puts Constants::Gradient::C -> 2
|
15
|
+
# puts Constants::Gradient::MINVALUE -> 0
|
16
|
+
# puts Constants::Gradient::MAXVALUE -> 2
|
17
|
+
# puts Constants::Gradient::NAMES -> [:B, :A, :C]
|
18
|
+
# puts Constants::Gradient[0] -> :B
|
19
|
+
# puts Constants::Gradient[1] -> :A
|
20
|
+
# puts Constants::Gradient[2] -> :C
|
21
|
+
|
22
|
+
def enum(*syms)
|
23
|
+
syms.each_index { |i|
|
24
|
+
const_set(syms[i], i)
|
25
|
+
}
|
26
|
+
const_set(:NAMES, syms || [])
|
27
|
+
const_set(:MINVALUE, syms==nil ? nil : 0)
|
28
|
+
const_set(:MAXVALUE, syms==nil ? nil : syms.length-1)
|
29
|
+
const_set(:VALUECOUNT, syms==nil ? nil : syms.length)
|
30
|
+
const_set(:ALL, syms==nil ? [] : (0..syms.length-1).to_a)
|
31
|
+
const_set(:HUMAN_NAMES, syms.map{|n| n.to_s.humanize} || [])
|
32
|
+
|
33
|
+
# this returns the enum name given the value
|
34
|
+
def self.[]( idx )
|
35
|
+
(idx.is_a? Integer) ? const_get(:NAMES)[idx] : nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.valid?(idx)
|
39
|
+
(idx.is_a? Integer) && (idx >= 0) && (idx <= const_get(:MAXVALUE))
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parse(name,default=nil)
|
43
|
+
return default if name.nil? || name.empty?
|
44
|
+
return default if not name = name.to_sym
|
45
|
+
result = const_get(:NAMES).index(name)
|
46
|
+
return result==nil ? default : result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,320 @@
|
|
1
|
+
String.class_eval do
|
2
|
+
def pad_left(value)
|
3
|
+
increase = value-self.length
|
4
|
+
return self if increase==0
|
5
|
+
if increase > 0
|
6
|
+
return self + ' '*increase
|
7
|
+
else
|
8
|
+
return self[0,value]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def pad_right(value)
|
13
|
+
increase = value-self.length
|
14
|
+
return self if increase==0
|
15
|
+
if increase > 0
|
16
|
+
return ' '*increase + self
|
17
|
+
else
|
18
|
+
return self[0,value]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Like chomp! but operates on the leading characters instead.
|
23
|
+
# The aString parameter would not normally be used.
|
24
|
+
def bite!(aValue=$/,aString=self)
|
25
|
+
if aString[0,aValue.length] == aValue
|
26
|
+
aString[0,aValue.length] = ''
|
27
|
+
return aString
|
28
|
+
else
|
29
|
+
return aString
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def bite(aValue=$/)
|
34
|
+
bite!(aValue,self.clone)
|
35
|
+
end
|
36
|
+
|
37
|
+
def begins_with?(aString)
|
38
|
+
self[0,aString.length]==aString
|
39
|
+
end
|
40
|
+
|
41
|
+
def ends_with?(aString)
|
42
|
+
self[-aString.length,aString.length]==aString
|
43
|
+
end
|
44
|
+
|
45
|
+
# for future methods
|
46
|
+
# def centre_bar(aChar = '-', indent = 6)
|
47
|
+
# (' '*indent) + aChar*(@width-(indent*2)) + (' '*indent)
|
48
|
+
# end
|
49
|
+
# def replace_string(aString,aCol,aSubString)
|
50
|
+
# return aString if aSubString==nil || aSubString==''
|
51
|
+
#
|
52
|
+
# aSubString = aSubString.to_s
|
53
|
+
# start_col = aCol < 0 ? 0 : aCol
|
54
|
+
# end_col = aCol+aSubString.length-1
|
55
|
+
# end_col = @width-1 if end_col >= @width
|
56
|
+
# source_len = end_col-start_col+1
|
57
|
+
# return aString if source_len <= 0 || end_col < 0 || start_col >= @width
|
58
|
+
# aString += ' '*((end_col+1) - aString.length) if aString.length < end_col+1
|
59
|
+
# aString[start_col,source_len] = aSubString[start_col-aCol,end_col-start_col+1]
|
60
|
+
# return aString
|
61
|
+
# end
|
62
|
+
|
63
|
+
def to_integer(aDefault=nil)
|
64
|
+
t = self.strip
|
65
|
+
return aDefault if t.empty? || !t.index(/^-{0,1}[0-9]+$/)
|
66
|
+
return t.to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_i?
|
70
|
+
self.to_integer(false) and true
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_float(aDefault=nil)
|
74
|
+
t = self.strip
|
75
|
+
return aDefault if !t =~ /(\+|-)?([0-9]+\.?[0-9]*|\.[0-9]+)([eE](\+|-)?[0-9]+)?/
|
76
|
+
return t.to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
def is_f?
|
80
|
+
self.to_float(false) and true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
Time.class_eval do
|
87
|
+
|
88
|
+
if !respond_to?(:change) # no activesupport loaded
|
89
|
+
def change(options)
|
90
|
+
::Time.send(
|
91
|
+
self.utc? ? :utc : :local,
|
92
|
+
options[:year] || self.year,
|
93
|
+
options[:month] || self.month,
|
94
|
+
options[:day] || self.day,
|
95
|
+
options[:hour] || self.hour,
|
96
|
+
options[:min] || (options[:hour] ? 0 : self.min),
|
97
|
+
options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
|
98
|
+
options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def seconds_since_midnight
|
103
|
+
self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
|
104
|
+
end
|
105
|
+
|
106
|
+
def beginning_of_day
|
107
|
+
(self - self.seconds_since_midnight).change(:usec => 0)
|
108
|
+
end
|
109
|
+
|
110
|
+
alias :midnight :beginning_of_day
|
111
|
+
alias :at_midnight :beginning_of_day
|
112
|
+
alias :at_beginning_of_day :beginning_of_day
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# offset of local machine from UTC, in seconds eg +9.hours
|
117
|
+
def self.local_offset
|
118
|
+
local(2000).utc_offset
|
119
|
+
end
|
120
|
+
|
121
|
+
def date
|
122
|
+
self.at_beginning_of_day
|
123
|
+
end
|
124
|
+
|
125
|
+
# index number of this day, from Time.at(0) + utc_offset
|
126
|
+
def day_number
|
127
|
+
(self.to_i+self.utc_offset) / 86400
|
128
|
+
end
|
129
|
+
|
130
|
+
# index number of this utc day
|
131
|
+
def day_number_utc
|
132
|
+
self.to_i / 86400
|
133
|
+
end
|
134
|
+
|
135
|
+
# the last microsecond of the day
|
136
|
+
def day_end
|
137
|
+
self.at_beginning_of_day + 86399.999999
|
138
|
+
end
|
139
|
+
|
140
|
+
def date_numeric
|
141
|
+
self.strftime('%Y%m%d')
|
142
|
+
end
|
143
|
+
|
144
|
+
# create a new Time from eg. "20081231"
|
145
|
+
def self.from_date_numeric(aString)
|
146
|
+
return nil unless aString
|
147
|
+
local(aString[0,4].to_i,aString[4,2].to_i,aString[6,2].to_i)
|
148
|
+
end
|
149
|
+
|
150
|
+
def time_numeric
|
151
|
+
self.strftime('%H%M%S')
|
152
|
+
end
|
153
|
+
|
154
|
+
def datetime_numeric
|
155
|
+
self.strftime('%Y%m%d-%H%M%S')
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_sql
|
159
|
+
self.strftime('%Y-%m-%d %H:%M:%S')
|
160
|
+
end
|
161
|
+
|
162
|
+
def to_w3c
|
163
|
+
utc.strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
module HashUtils
|
168
|
+
def filter_include!(aKeys,aHash=nil)
|
169
|
+
aHash ||= self
|
170
|
+
|
171
|
+
if aKeys.is_a? Regexp
|
172
|
+
return aHash.delete_if {|k,v| not k =~ aKeys }
|
173
|
+
else
|
174
|
+
aKeys = [aKeys] unless aKeys.is_a? Array
|
175
|
+
return aHash.clear if aKeys.empty?
|
176
|
+
return aHash.delete_if {|key, value| !((aKeys.include?(key)) || (key.is_a?(Symbol) and aKeys.include?(key.to_s)) || (key.is_a?(String) and aKeys.include?(key.to_sym)))}
|
177
|
+
return aHash # last resort
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def filter_include(aKeys,aHash=nil)
|
182
|
+
aHash ||= self
|
183
|
+
filter_include!(aKeys,aHash.clone)
|
184
|
+
end
|
185
|
+
|
186
|
+
def filter_exclude!(aKeys,aHash=nil)
|
187
|
+
aHash ||= self
|
188
|
+
|
189
|
+
if aKeys.is_a? Regexp
|
190
|
+
return aHash.delete_if {|k,v| k =~ aKeys }
|
191
|
+
else
|
192
|
+
aKeys = [aKeys] unless aKeys.is_a? Array
|
193
|
+
return aHash if aKeys.empty?
|
194
|
+
return aHash.delete_if {|key, value| ((aKeys.include?(key)) || (key.is_a?(Symbol) and aKeys.include?(key.to_s)) || (key.is_a?(String) and aKeys.include?(key.to_sym)))}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def filter_exclude(aKeys,aHash=nil)
|
199
|
+
aHash ||= self
|
200
|
+
filter_exclude!(aKeys,aHash.clone)
|
201
|
+
end
|
202
|
+
|
203
|
+
def has_values_for?(aKeys,aHash=nil)
|
204
|
+
aHash ||= self
|
205
|
+
# check all keys exist in aHash and their values are not nil
|
206
|
+
aKeys.all? { |k,v| aHash[k] }
|
207
|
+
end
|
208
|
+
|
209
|
+
# give a block to execute without the given key in this hash
|
210
|
+
# It will be replaced after the block (guaranteed by ensure)
|
211
|
+
# eg.
|
212
|
+
# hash.without_key(:blah) do |aHash|
|
213
|
+
# puts aHash.inspect
|
214
|
+
# end
|
215
|
+
def without_key(aKey)
|
216
|
+
temp = nil
|
217
|
+
h = self
|
218
|
+
begin
|
219
|
+
if h.include?(aKey)
|
220
|
+
temp = [aKey,h.delete(aKey)]
|
221
|
+
end
|
222
|
+
result = yield(h)
|
223
|
+
ensure
|
224
|
+
h[temp[0]] = temp[1] if temp
|
225
|
+
end
|
226
|
+
return result
|
227
|
+
end
|
228
|
+
|
229
|
+
def symbolize_keys
|
230
|
+
result = {}
|
231
|
+
self.each { |k,v| k.is_a?(String) ? result[k.to_sym] = v : result[k] = v }
|
232
|
+
return result
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
Hash.class_eval do
|
238
|
+
include HashUtils
|
239
|
+
end
|
240
|
+
|
241
|
+
if defined? HashWithIndifferentAccess
|
242
|
+
HashWithIndifferentAccess.class_eval do
|
243
|
+
include HashUtils
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
module ArrayUtils
|
248
|
+
def filter_include!(aValues,aArray=nil)
|
249
|
+
aArray ||= self
|
250
|
+
if aValues.is_a? Array
|
251
|
+
return aArray if aValues.empty?
|
252
|
+
return aArray.delete_if {|v| not aValues.include? v }
|
253
|
+
elsif aValues.is_a? Regexp
|
254
|
+
return aArray.delete_if {|v| not v =~ aValues }
|
255
|
+
else
|
256
|
+
return filter_include!([aValues],aArray)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def filter_include(aValues,aArray=nil)
|
261
|
+
aArray ||= self
|
262
|
+
filter_include!(aValues,aArray.clone)
|
263
|
+
end
|
264
|
+
|
265
|
+
def filter_exclude!(aValues,aArray=nil)
|
266
|
+
aArray ||= self
|
267
|
+
if aValues.is_a? Array
|
268
|
+
return aArray if aValues.empty?
|
269
|
+
return aArray.delete_if {|v| aValues.include? v }
|
270
|
+
elsif aValues.is_a? Regexp
|
271
|
+
return aArray.delete_if {|v| v =~ aValues }
|
272
|
+
else
|
273
|
+
return filter_exclude!([aValues],aArray)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def filter_exclude(aValues,aArray=nil)
|
278
|
+
aArray ||= self
|
279
|
+
filter_exclude!(aValues,aArray.clone)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
Array.class_eval do
|
284
|
+
include ArrayUtils
|
285
|
+
|
286
|
+
# fixes a memory leak in shift in Ruby 1.8 - should be fixed in 1.9
|
287
|
+
# see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/216055
|
288
|
+
def shift()
|
289
|
+
delete_at(0)
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
|
294
|
+
Kernel.class_eval do
|
295
|
+
def is_windows?
|
296
|
+
RUBY_PLATFORM =~ /(win|w)32$/ ? true : false
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
if defined? ActiveRecord
|
301
|
+
ActiveRecord::Base.class_eval do
|
302
|
+
|
303
|
+
def self.find_any_id(aId)
|
304
|
+
with_exclusive_scope { find(:first, {:conditions => {:id => aId}}) }
|
305
|
+
end
|
306
|
+
|
307
|
+
def self.find_any_all(aOptions={})
|
308
|
+
with_exclusive_scope { find(:all, aOptions) }
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.find_ids(aIds)
|
312
|
+
find(:all, {:conditions=> ["id in (?)",aIds.join(',')]})
|
313
|
+
end
|
314
|
+
|
315
|
+
def self.find_any_ids(aIds)
|
316
|
+
with_exclusive_scope { find(:all, {:conditions=> ["id in (?)",aIds.join(',')]}) }
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|