procemon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -0
- data/Gemfile.lock +72 -0
- data/LICENSE.txt +20 -0
- data/README.md +4 -0
- data/README.rdoc +19 -0
- data/Rakefile +170 -0
- data/VERSION +1 -0
- data/dump/macaddr.rb +95 -0
- data/dump/uuid.rb +324 -0
- data/lib/procemon.rb +55 -0
- data/lib/procemon/function/application.rb +20 -0
- data/lib/procemon/function/argv.rb +84 -0
- data/lib/procemon/function/daemon.rb +188 -0
- data/lib/procemon/function/documentation.rb +10 -0
- data/lib/procemon/function/eval.rb +76 -0
- data/lib/procemon/function/meta/inject_methods.rb +74 -0
- data/lib/procemon/function/name.rb +13 -0
- data/lib/procemon/function/port.rb +35 -0
- data/lib/procemon/function/require.rb +241 -0
- data/lib/procemon/function/str2duck.rb +85 -0
- data/lib/procemon/function/systemu.rb +360 -0
- data/lib/procemon/function/tmp_dir.rb +20 -0
- data/lib/procemon/mpatch/array.rb +59 -0
- data/lib/procemon/mpatch/class.rb +85 -0
- data/lib/procemon/mpatch/exception.rb +0 -0
- data/lib/procemon/mpatch/file.rb +48 -0
- data/lib/procemon/mpatch/hash.rb +82 -0
- data/lib/procemon/mpatch/kernel.rb +9 -0
- data/lib/procemon/mpatch/object.rb +172 -0
- data/lib/procemon/mpatch/process.rb +14 -0
- data/lib/procemon/mpatch/random.rb +43 -0
- data/lib/procemon/mpatch/string.rb +73 -0
- data/lib/procemon/mpatch/yml.rb +11 -0
- data/procemon.gemspec +58 -0
- data/test/test.rb +17 -0
- metadata +86 -0
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.0"
|
12
|
+
gem "jeweler", "~> 1.8.7"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.0.0)
|
5
|
+
i18n (~> 0.6, >= 0.6.4)
|
6
|
+
minitest (~> 4.2)
|
7
|
+
multi_json (~> 1.3)
|
8
|
+
thread_safe (~> 0.1)
|
9
|
+
tzinfo (~> 0.3.37)
|
10
|
+
addressable (2.3.5)
|
11
|
+
atomic (1.1.14)
|
12
|
+
builder (3.2.2)
|
13
|
+
faraday (0.8.8)
|
14
|
+
multipart-post (~> 1.2.0)
|
15
|
+
git (1.2.6)
|
16
|
+
github_api (0.10.1)
|
17
|
+
addressable
|
18
|
+
faraday (~> 0.8.1)
|
19
|
+
hashie (>= 1.2)
|
20
|
+
multi_json (~> 1.4)
|
21
|
+
nokogiri (~> 1.5.2)
|
22
|
+
oauth2
|
23
|
+
hashie (2.0.5)
|
24
|
+
highline (1.6.20)
|
25
|
+
httpauth (0.2.0)
|
26
|
+
i18n (0.6.5)
|
27
|
+
jeweler (1.8.8)
|
28
|
+
builder
|
29
|
+
bundler (~> 1.0)
|
30
|
+
git (>= 1.2.5)
|
31
|
+
github_api (= 0.10.1)
|
32
|
+
highline (>= 1.6.15)
|
33
|
+
nokogiri (= 1.5.10)
|
34
|
+
rake
|
35
|
+
rdoc
|
36
|
+
json (1.8.0)
|
37
|
+
jwt (0.1.8)
|
38
|
+
multi_json (>= 1.5)
|
39
|
+
minitest (4.7.5)
|
40
|
+
multi_json (1.8.2)
|
41
|
+
multi_xml (0.5.5)
|
42
|
+
multipart-post (1.2.0)
|
43
|
+
nokogiri (1.5.10)
|
44
|
+
oauth2 (0.9.2)
|
45
|
+
faraday (~> 0.8)
|
46
|
+
httpauth (~> 0.2)
|
47
|
+
jwt (~> 0.1.4)
|
48
|
+
multi_json (~> 1.0)
|
49
|
+
multi_xml (~> 0.5)
|
50
|
+
rack (~> 1.2)
|
51
|
+
rack (1.5.2)
|
52
|
+
rake (10.1.0)
|
53
|
+
rdoc (3.12.2)
|
54
|
+
json (~> 1.4)
|
55
|
+
shoulda (3.5.0)
|
56
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
57
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
58
|
+
shoulda-context (1.1.5)
|
59
|
+
shoulda-matchers (2.4.0)
|
60
|
+
activesupport (>= 3.0.0)
|
61
|
+
thread_safe (0.1.3)
|
62
|
+
atomic
|
63
|
+
tzinfo (0.3.38)
|
64
|
+
|
65
|
+
PLATFORMS
|
66
|
+
ruby
|
67
|
+
|
68
|
+
DEPENDENCIES
|
69
|
+
bundler (~> 1.0)
|
70
|
+
jeweler (~> 1.8.7)
|
71
|
+
rdoc (~> 3.12)
|
72
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Adam Luzsi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= procemon
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to procemon
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2013 Adam Luzsi. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#require 'bundler/gem_helper'
|
2
|
+
require 'bundler'
|
3
|
+
module Bundler
|
4
|
+
class GemHelper
|
5
|
+
include Rake::DSL if defined? Rake::DSL
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# set when install'd.
|
9
|
+
attr_accessor :instance
|
10
|
+
|
11
|
+
def install_tasks(opts = {})
|
12
|
+
new(opts[:dir], opts[:name]).install
|
13
|
+
end
|
14
|
+
|
15
|
+
def gemspec(&block)
|
16
|
+
gemspec = instance.gemspec
|
17
|
+
block.call(gemspec) if block
|
18
|
+
gemspec
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :spec_path, :base, :gemspec
|
23
|
+
|
24
|
+
def initialize(base = nil, name = nil)
|
25
|
+
Bundler.ui = UI::Shell.new
|
26
|
+
@base = (base ||= Dir.pwd)
|
27
|
+
gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
|
28
|
+
#raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
|
29
|
+
@spec_path = gemspecs.first
|
30
|
+
@gemspec = Bundler.load_gemspec(@spec_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def install
|
34
|
+
built_gem_path = nil
|
35
|
+
|
36
|
+
desc "Build #{name}-#{version}.gem into the pkg directory."
|
37
|
+
task 'build' do
|
38
|
+
built_gem_path = build_gem
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Build and install #{name}-#{version}.gem into system gems."
|
42
|
+
task 'install' => 'build' do
|
43
|
+
install_gem(built_gem_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
#desc "NOT this: Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems"
|
47
|
+
#task 'release' => 'build' do
|
48
|
+
# #release_gem(built_gem_path)
|
49
|
+
#end
|
50
|
+
|
51
|
+
GemHelper.instance = self
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_gem
|
55
|
+
file_name = nil
|
56
|
+
sh("gem build -V '#{spec_path}'") { |out, code|
|
57
|
+
file_name = File.basename(built_gem_path)
|
58
|
+
FileUtils.mkdir_p(File.join(base, 'pkg'))
|
59
|
+
FileUtils.mv(built_gem_path, 'pkg')
|
60
|
+
Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
|
61
|
+
}
|
62
|
+
File.join(base, 'pkg', file_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def install_gem(built_gem_path=nil)
|
66
|
+
built_gem_path ||= build_gem
|
67
|
+
out, _ = sh_with_code("gem install '#{built_gem_path}' --local")
|
68
|
+
raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
|
69
|
+
Bundler.ui.confirm "#{name} (#{version}) installed."
|
70
|
+
end
|
71
|
+
|
72
|
+
def release_gem(built_gem_path=nil)
|
73
|
+
#guard_clean
|
74
|
+
#built_gem_path ||= build_gem
|
75
|
+
#tag_version { git_push } unless already_tagged?
|
76
|
+
#rubygem_push(built_gem_path) if gem_push?
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
def rubygem_push(path)
|
81
|
+
if Pathname.new("~/.gem/credentials").expand_path.exist?
|
82
|
+
sh("gem push '#{path}'")
|
83
|
+
Bundler.ui.confirm "Pushed #{name} #{version} to rubygems.org."
|
84
|
+
else
|
85
|
+
raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def built_gem_path
|
90
|
+
Dir[File.join(base, "#{name}-*.gem")].sort_by{|f| File.mtime(f)}.last
|
91
|
+
end
|
92
|
+
|
93
|
+
def git_push
|
94
|
+
perform_git_push
|
95
|
+
perform_git_push ' --tags'
|
96
|
+
Bundler.ui.confirm "Pushed git commits and tags."
|
97
|
+
end
|
98
|
+
|
99
|
+
def perform_git_push(options = '')
|
100
|
+
cmd = "git push #{options}"
|
101
|
+
out, code = sh_with_code(cmd)
|
102
|
+
raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def already_tagged?
|
106
|
+
if sh('git tag').split(/\n/).include?(version_tag)
|
107
|
+
Bundler.ui.confirm "Tag #{version_tag} has already been created."
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def guard_clean
|
113
|
+
clean? && committed? or raise("There are files that need to be committed first.")
|
114
|
+
end
|
115
|
+
|
116
|
+
def clean?
|
117
|
+
sh_with_code("git diff --exit-code")[1] == 0
|
118
|
+
end
|
119
|
+
|
120
|
+
def committed?
|
121
|
+
sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
|
122
|
+
end
|
123
|
+
|
124
|
+
def tag_version
|
125
|
+
sh "git tag -a -m \"Version #{version}\" #{version_tag}"
|
126
|
+
Bundler.ui.confirm "Tagged #{version_tag}."
|
127
|
+
yield if block_given?
|
128
|
+
rescue
|
129
|
+
Bundler.ui.error "Untagging #{version_tag} due to error."
|
130
|
+
sh_with_code "git tag -d #{version_tag}"
|
131
|
+
raise
|
132
|
+
end
|
133
|
+
|
134
|
+
def version
|
135
|
+
gemspec.version
|
136
|
+
end
|
137
|
+
|
138
|
+
def version_tag
|
139
|
+
"v#{version}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def name
|
143
|
+
gemspec.name
|
144
|
+
end
|
145
|
+
|
146
|
+
def sh(cmd, &block)
|
147
|
+
out, code = sh_with_code(cmd, &block)
|
148
|
+
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
|
149
|
+
end
|
150
|
+
|
151
|
+
def sh_with_code(cmd, &block)
|
152
|
+
cmd << " 2>&1"
|
153
|
+
outbuf = ''
|
154
|
+
Bundler.ui.debug(cmd)
|
155
|
+
Dir.chdir(base) {
|
156
|
+
outbuf = `#{cmd}`
|
157
|
+
if $? == 0
|
158
|
+
block.call(outbuf) if block
|
159
|
+
end
|
160
|
+
}
|
161
|
+
[outbuf, $?]
|
162
|
+
end
|
163
|
+
|
164
|
+
def gem_push?
|
165
|
+
! %w{n no nil false off 0}.include?(ENV['gem_push'].to_s.downcase)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
Bundler::GemHelper.install_tasks
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/dump/macaddr.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#
|
2
|
+
# Cross platform MAC address determination. Works for:
|
3
|
+
# * /sbin/ifconfig
|
4
|
+
# * /bin/ifconfig
|
5
|
+
# * ifconfig
|
6
|
+
# * ipconfig /all
|
7
|
+
#
|
8
|
+
# To return the first MAC address on the system:
|
9
|
+
#
|
10
|
+
# Mac.address
|
11
|
+
#
|
12
|
+
# To return an array of all MAC addresses:
|
13
|
+
#
|
14
|
+
# Mac.address.list
|
15
|
+
#
|
16
|
+
#begin
|
17
|
+
# require 'rubygems'
|
18
|
+
# require 'systemu'
|
19
|
+
#rescue LoadError
|
20
|
+
# nil
|
21
|
+
#end
|
22
|
+
|
23
|
+
|
24
|
+
module Mac
|
25
|
+
VERSION = '1.6.1'
|
26
|
+
|
27
|
+
def Mac.version
|
28
|
+
::Mac::VERSION
|
29
|
+
end
|
30
|
+
|
31
|
+
def Mac.dependencies
|
32
|
+
{
|
33
|
+
'systemu' => [ 'systemu' , '~> 2.5.0' ]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
#
|
41
|
+
# Accessor for the system's first MAC address, requires a call to #address
|
42
|
+
# first
|
43
|
+
|
44
|
+
attr_accessor "mac_address"
|
45
|
+
|
46
|
+
#
|
47
|
+
# Discovers and returns the system's MAC addresses. Returns the first
|
48
|
+
# MAC address, and includes an accessor #list for the remaining addresses:
|
49
|
+
#
|
50
|
+
# Mac.addr # => first address
|
51
|
+
# Mac.addr.list # => all addresses
|
52
|
+
|
53
|
+
def address
|
54
|
+
return @mac_address if defined? @mac_address and @mac_address
|
55
|
+
re = %r/[^:\-](?:[0-9A-F][0-9A-F][:\-]){5}[0-9A-F][0-9A-F][^:\-]/io
|
56
|
+
cmds = '/sbin/ifconfig', '/bin/ifconfig', 'ifconfig', 'ipconfig /all', 'cat /sys/class/net/*/address'
|
57
|
+
|
58
|
+
null = test(?e, '/dev/null') ? '/dev/null' : 'NUL'
|
59
|
+
|
60
|
+
output = nil
|
61
|
+
cmds.each do |cmd|
|
62
|
+
status, stdout, stderr = systemu(cmd) rescue next
|
63
|
+
next unless stdout and stdout.size > 0
|
64
|
+
output = stdout and break
|
65
|
+
end
|
66
|
+
raise "all of #{ cmds.join ' ' } failed" unless output
|
67
|
+
|
68
|
+
@mac_address = parse(output)
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse(output)
|
72
|
+
lines = output.split(/\n/)
|
73
|
+
|
74
|
+
candidates = lines.select{|line| line =~ RE}
|
75
|
+
raise 'no mac address candidates' unless candidates.first
|
76
|
+
candidates.map!{|c| c[RE].strip}
|
77
|
+
|
78
|
+
maddr = candidates.first
|
79
|
+
raise 'no mac address found' unless maddr
|
80
|
+
|
81
|
+
maddr.strip!
|
82
|
+
maddr.instance_eval{ @list = candidates; def list() @list end }
|
83
|
+
maddr
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Shorter alias for #address
|
88
|
+
|
89
|
+
alias_method "addr", "address"
|
90
|
+
end
|
91
|
+
|
92
|
+
RE = %r/(?:[^:\-]|\A)(?:[0-9A-F][0-9A-F][:\-]){5}[0-9A-F][0-9A-F](?:[^:\-]|\Z)/io
|
93
|
+
end
|
94
|
+
|
95
|
+
MacAddr = Macaddr = Mac
|
data/dump/uuid.rb
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright(c) 2005 URABE, Shyouhei.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this code, to deal in the code without restriction, including without
|
7
|
+
# limitation the rights to use, copy, modify, merge, publish, distribute,
|
8
|
+
# sublicense, and/or sell copies of the code, and to permit persons to whom the
|
9
|
+
# code is furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the code.
|
13
|
+
#
|
14
|
+
# THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# CODE.
|
21
|
+
|
22
|
+
%w[
|
23
|
+
digest/md5
|
24
|
+
digest/sha1
|
25
|
+
tmpdir
|
26
|
+
].each do |f|
|
27
|
+
require f
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pure ruby UUID generator, which is compatible with RFC4122
|
31
|
+
class UUID
|
32
|
+
# UUID epoch is 15th Oct. 1582
|
33
|
+
UNIXEpoch = 0x01B21DD213814000 # in 100-nanoseconds resolution
|
34
|
+
|
35
|
+
private_class_method :new
|
36
|
+
|
37
|
+
private
|
38
|
+
def initialize str
|
39
|
+
tmp = str.unpack "C*"
|
40
|
+
@num = tmp.inject do |r, i|
|
41
|
+
r * 256 | i
|
42
|
+
end
|
43
|
+
@num.freeze
|
44
|
+
self.freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
public
|
48
|
+
|
49
|
+
def raw_bytes
|
50
|
+
ret = String.new
|
51
|
+
tmp = @num
|
52
|
+
16.times do |i|
|
53
|
+
x, y = tmp.divmod 256
|
54
|
+
ret << y
|
55
|
+
tmp = x
|
56
|
+
end
|
57
|
+
ret.reverse!
|
58
|
+
ret
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
def mask ver, str # :nodoc:
|
63
|
+
ver = ver & 15
|
64
|
+
v = str[6].ord
|
65
|
+
v &= 0b0000_1111
|
66
|
+
v |= ver << 4
|
67
|
+
str[6] = v.chr
|
68
|
+
r = str[8].ord
|
69
|
+
r &= 0b0011_1111
|
70
|
+
r |= 0b1000_0000
|
71
|
+
str[8] = r.chr
|
72
|
+
str
|
73
|
+
end
|
74
|
+
|
75
|
+
def prand # :nodoc:
|
76
|
+
rand 0x100000000
|
77
|
+
end
|
78
|
+
|
79
|
+
private :mask, :prand
|
80
|
+
|
81
|
+
# UUID generation using SHA1. Recommended over create_md5.
|
82
|
+
# Namespace object is another UUID, some of them are pre-defined below.
|
83
|
+
def create_sha1 str, namespace
|
84
|
+
sha1 = Digest::SHA1.new
|
85
|
+
sha1.update namespace.raw_bytes
|
86
|
+
sha1.update str
|
87
|
+
sum = sha1.digest
|
88
|
+
raw = mask 5, sum[0..15]
|
89
|
+
new raw
|
90
|
+
end
|
91
|
+
|
92
|
+
# UUID generation using MD5 (for backward compat.)
|
93
|
+
def create_md5 str, namespace
|
94
|
+
md5 = Digest::MD5.new
|
95
|
+
md5.update namespace.raw_bytes
|
96
|
+
md5.update str
|
97
|
+
sum = md5.digest
|
98
|
+
raw = mask 3, sum[0..16]
|
99
|
+
new raw
|
100
|
+
end
|
101
|
+
|
102
|
+
# UUID generation using random-number generator. From it's random
|
103
|
+
# nature, there's no warranty that the created ID is really universaly
|
104
|
+
# unique.
|
105
|
+
def create_random
|
106
|
+
rnd = [prand, prand, prand, prand].pack "N4"
|
107
|
+
raw = mask 4, rnd
|
108
|
+
new raw
|
109
|
+
end
|
110
|
+
|
111
|
+
def read_state fp # :nodoc:
|
112
|
+
fp.rewind
|
113
|
+
Marshal.load fp.read
|
114
|
+
end
|
115
|
+
|
116
|
+
def write_state fp, c, m # :nodoc:
|
117
|
+
fp.rewind
|
118
|
+
str = Marshal.dump [c, m]
|
119
|
+
fp.write str
|
120
|
+
end
|
121
|
+
|
122
|
+
private :read_state, :write_state
|
123
|
+
STATE_FILE = 'ruby-uuid'
|
124
|
+
|
125
|
+
# create the "version 1" UUID with current system clock, current UTC
|
126
|
+
# timestamp, and the IEEE 802 address (so-called MAC address).
|
127
|
+
#
|
128
|
+
# Speed notice: it's slow. It writes some data into hard drive on every
|
129
|
+
# invokation. If you want to speed this up, try remounting tmpdir with a
|
130
|
+
# memory based filesystem (such as tmpfs). STILL slow? then no way but
|
131
|
+
# rewrite it with c :)
|
132
|
+
def create *args
|
133
|
+
|
134
|
+
if args[0].class.to_s == "Hash"
|
135
|
+
args= Hash[*args]
|
136
|
+
clock = args[:clock] || nil
|
137
|
+
time = args[:time] || Time.now
|
138
|
+
mac_addr= args[:mac] || nil
|
139
|
+
else
|
140
|
+
clock = args[0] || nil
|
141
|
+
time = args[1] || Time.now
|
142
|
+
mac_addr= args[2] || nil
|
143
|
+
end
|
144
|
+
|
145
|
+
c = t = m = nil
|
146
|
+
Dir.chdir Dir.tmpdir do
|
147
|
+
unless FileTest.exist? STATE_FILE then
|
148
|
+
# Generate a pseudo MAC address because we have no pure-ruby way
|
149
|
+
# to know the MAC address of the NIC this system uses. Note
|
150
|
+
# that cheating with pseudo arresses here is completely legal:
|
151
|
+
# see Section 4.5 of RFC4122 for details.
|
152
|
+
sha1 = Digest::SHA1.new
|
153
|
+
256.times do
|
154
|
+
r = [prand].pack "N"
|
155
|
+
sha1.update r
|
156
|
+
end
|
157
|
+
ary = sha1.digest.bytes.to_a
|
158
|
+
node = ary.last 6
|
159
|
+
node[0] |= 0x01 # multicast bit
|
160
|
+
node = node.pack "C*"
|
161
|
+
k = rand 0x40000
|
162
|
+
open STATE_FILE, 'w' do |fp|
|
163
|
+
fp.flock IO::LOCK_EX
|
164
|
+
write_state fp, k, node
|
165
|
+
fp.chmod 0o777 # must be world writable
|
166
|
+
end
|
167
|
+
end
|
168
|
+
open STATE_FILE, 'r+' do |fp|
|
169
|
+
fp.flock IO::LOCK_EX
|
170
|
+
c, m = read_state fp
|
171
|
+
c += 1 # important; increment here
|
172
|
+
write_state fp, c, m
|
173
|
+
end
|
174
|
+
end
|
175
|
+
c = clock & 0b11_1111_1111_1111 if clock
|
176
|
+
m = mac_addr if mac_addr
|
177
|
+
time = Time.at time if time.is_a? Float
|
178
|
+
case time
|
179
|
+
when Time
|
180
|
+
t = time.to_i * 10_000_000 + time.tv_usec * 10 + UNIXEpoch
|
181
|
+
when Integer
|
182
|
+
t = time + UNIXEpoch
|
183
|
+
else
|
184
|
+
raise TypeError, "cannot convert ``#{time}'' into Time."
|
185
|
+
end
|
186
|
+
|
187
|
+
tl = t & 0xFFFF_FFFF
|
188
|
+
tm = t >> 32
|
189
|
+
tm = tm & 0xFFFF
|
190
|
+
th = t >> 48
|
191
|
+
th = th & 0b0000_1111_1111_1111
|
192
|
+
th = th | 0b0001_0000_0000_0000
|
193
|
+
cl = c & 0b0000_0000_1111_1111
|
194
|
+
ch = c & 0b0011_1111_0000_0000
|
195
|
+
ch = ch >> 8
|
196
|
+
ch = ch | 0b1000_0000
|
197
|
+
pack tl, tm, th, ch, cl, m
|
198
|
+
end
|
199
|
+
|
200
|
+
# A simple GUID parser: just ignores unknown characters and convert
|
201
|
+
# hexadecimal dump into 16-octet object.
|
202
|
+
def parse obj
|
203
|
+
str = obj.to_s.sub %r/\Aurn:uuid:/, ''
|
204
|
+
str.gsub! %r/[^0-9A-Fa-f]/, ''
|
205
|
+
raw = [str[0..31]].pack 'H*'
|
206
|
+
new raw
|
207
|
+
end
|
208
|
+
|
209
|
+
# The 'primitive constructor' of this class
|
210
|
+
# Note UUID.pack(uuid.unpack) == uuid
|
211
|
+
def pack tl, tm, th, ch, cl, n
|
212
|
+
raw = [tl, tm, th, ch, cl, n].pack "NnnCCa6"
|
213
|
+
new raw
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# The 'primitive deconstructor', or the dual to pack.
|
218
|
+
# Note UUID.pack(uuid.unpack) == uuid
|
219
|
+
def unpack
|
220
|
+
raw_bytes.unpack "NnnCCa6"
|
221
|
+
end
|
222
|
+
|
223
|
+
# The timestamp of this UUID.
|
224
|
+
# Throws RageError if that time exceeds UNIX time range
|
225
|
+
def time
|
226
|
+
a = unpack
|
227
|
+
tl = a[0]
|
228
|
+
tm = a[1]
|
229
|
+
th = a[2] & 0x0FFF
|
230
|
+
t = tl
|
231
|
+
t += tm << 32
|
232
|
+
t += th << 48
|
233
|
+
t -= UNIXEpoch
|
234
|
+
tv_sec = t / 10_000_000
|
235
|
+
t -= tv_sec * 10_000_000
|
236
|
+
tv_usec = t / 10
|
237
|
+
Time.at tv_sec, tv_usec
|
238
|
+
end
|
239
|
+
|
240
|
+
# The version of this UUID
|
241
|
+
def version
|
242
|
+
v = unpack[2] & 0b1111_0000_0000_0000
|
243
|
+
v >> 12
|
244
|
+
end
|
245
|
+
|
246
|
+
# The clock sequence of this UUID
|
247
|
+
def clock
|
248
|
+
a = unpack
|
249
|
+
ch = a[3] & 0b0001_1111
|
250
|
+
cl = a[4]
|
251
|
+
c = cl
|
252
|
+
c += ch << 8
|
253
|
+
c
|
254
|
+
end
|
255
|
+
|
256
|
+
# The IEEE 802 address in a hexadecimal format
|
257
|
+
def node
|
258
|
+
m = unpack[5].unpack 'C*'
|
259
|
+
'%02x%02x%02x%02x%02x%02x' % m
|
260
|
+
end
|
261
|
+
alias mac_address node
|
262
|
+
alias ieee802 node
|
263
|
+
|
264
|
+
# Generate the string representation (a.k.a GUID) of this UUID
|
265
|
+
def to_s
|
266
|
+
a = unpack
|
267
|
+
a[-1] = mac_address
|
268
|
+
"%08x-%04x-%04x-%02x%02x-%s" % a
|
269
|
+
end
|
270
|
+
alias guid to_s
|
271
|
+
|
272
|
+
# Convert into a RFC4122-comforming URN representation
|
273
|
+
def to_uri
|
274
|
+
"urn:uuid:" + self.to_s
|
275
|
+
end
|
276
|
+
alias urn to_uri
|
277
|
+
alias inspect to_uri
|
278
|
+
|
279
|
+
# Convert into 128-bit unsigned integer
|
280
|
+
# Typically a Bignum instance, but can be a Fixnum.
|
281
|
+
def to_int
|
282
|
+
@num
|
283
|
+
end
|
284
|
+
alias to_i to_int
|
285
|
+
|
286
|
+
# Two UUIDs are said to be equal if and only if their (byte-order
|
287
|
+
# canonicalized) integer representations are equivallent. Refer RFC4122 for
|
288
|
+
# details.
|
289
|
+
def == other
|
290
|
+
to_i == other.to_i
|
291
|
+
end
|
292
|
+
alias eql? ==
|
293
|
+
|
294
|
+
# Two identical UUIDs should have same hash
|
295
|
+
def hash
|
296
|
+
to_i
|
297
|
+
end
|
298
|
+
|
299
|
+
include Comparable
|
300
|
+
# UUIDs are comparable (don't know what benefits are there, though).
|
301
|
+
def <=> other
|
302
|
+
to_s <=> other.to_s
|
303
|
+
end
|
304
|
+
|
305
|
+
# Pre-defined UUID Namespaces described in RFC4122 Appendix C.
|
306
|
+
NameSpace_DNS = parse "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
307
|
+
NameSpace_URL = parse "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
|
308
|
+
NameSpace_OID = parse "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
|
309
|
+
NameSpace_X500 = parse "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
|
310
|
+
|
311
|
+
# The Nil UUID in RFC4122 Section 4.1.7
|
312
|
+
Nil = parse "00000000-0000-0000-0000-000000000000"
|
313
|
+
end
|
314
|
+
|
315
|
+
# Local Variables:
|
316
|
+
# mode: ruby
|
317
|
+
# coding: utf-8
|
318
|
+
# indent-tabs-mode: t
|
319
|
+
# tab-width: 3
|
320
|
+
# ruby-indent-level: 3
|
321
|
+
# fill-column: 79
|
322
|
+
# default-justification: full
|
323
|
+
# End:
|
324
|
+
# vi: ts=3 sw=3
|