measure 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +8 -0
- data/COPYING +675 -0
- data/COPYING.LIB +166 -0
- data/ChangeLog +10 -0
- data/README +19 -0
- data/Rakefile +150 -0
- data/TODO +13 -0
- data/lib/measure.rb +348 -0
- data/lib/measure/length.rb +59 -0
- data/lib/measure/support.rb +52 -0
- data/lib/measure/version.rb +17 -0
- data/spec/measure_spec.rb +104 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +4 -0
- metadata +73 -0
data/COPYING.LIB
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
2
|
+
Version 3, 29 June 2007
|
3
|
+
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
6
|
+
of this license document, but changing it is not allowed.
|
7
|
+
|
8
|
+
|
9
|
+
This version of the GNU Lesser General Public License incorporates
|
10
|
+
the terms and conditions of version 3 of the GNU General Public
|
11
|
+
License, supplemented by the additional permissions listed below.
|
12
|
+
|
13
|
+
0. Additional Definitions.
|
14
|
+
|
15
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
16
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
17
|
+
General Public License.
|
18
|
+
|
19
|
+
"The Library" refers to a covered work governed by this License,
|
20
|
+
other than an Application or a Combined Work as defined below.
|
21
|
+
|
22
|
+
An "Application" is any work that makes use of an interface provided
|
23
|
+
by the Library, but which is not otherwise based on the Library.
|
24
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
25
|
+
of using an interface provided by the Library.
|
26
|
+
|
27
|
+
A "Combined Work" is a work produced by combining or linking an
|
28
|
+
Application with the Library. The particular version of the Library
|
29
|
+
with which the Combined Work was made is also called the "Linked
|
30
|
+
Version".
|
31
|
+
|
32
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
33
|
+
Corresponding Source for the Combined Work, excluding any source code
|
34
|
+
for portions of the Combined Work that, considered in isolation, are
|
35
|
+
based on the Application, and not on the Linked Version.
|
36
|
+
|
37
|
+
The "Corresponding Application Code" for a Combined Work means the
|
38
|
+
object code and/or source code for the Application, including any data
|
39
|
+
and utility programs needed for reproducing the Combined Work from the
|
40
|
+
Application, but excluding the System Libraries of the Combined Work.
|
41
|
+
|
42
|
+
1. Exception to Section 3 of the GNU GPL.
|
43
|
+
|
44
|
+
You may convey a covered work under sections 3 and 4 of this License
|
45
|
+
without being bound by section 3 of the GNU GPL.
|
46
|
+
|
47
|
+
2. Conveying Modified Versions.
|
48
|
+
|
49
|
+
If you modify a copy of the Library, and, in your modifications, a
|
50
|
+
facility refers to a function or data to be supplied by an Application
|
51
|
+
that uses the facility (other than as an argument passed when the
|
52
|
+
facility is invoked), then you may convey a copy of the modified
|
53
|
+
version:
|
54
|
+
|
55
|
+
a) under this License, provided that you make a good faith effort to
|
56
|
+
ensure that, in the event an Application does not supply the
|
57
|
+
function or data, the facility still operates, and performs
|
58
|
+
whatever part of its purpose remains meaningful, or
|
59
|
+
|
60
|
+
b) under the GNU GPL, with none of the additional permissions of
|
61
|
+
this License applicable to that copy.
|
62
|
+
|
63
|
+
3. Object Code Incorporating Material from Library Header Files.
|
64
|
+
|
65
|
+
The object code form of an Application may incorporate material from
|
66
|
+
a header file that is part of the Library. You may convey such object
|
67
|
+
code under terms of your choice, provided that, if the incorporated
|
68
|
+
material is not limited to numerical parameters, data structure
|
69
|
+
layouts and accessors, or small macros, inline functions and templates
|
70
|
+
(ten or fewer lines in length), you do both of the following:
|
71
|
+
|
72
|
+
a) Give prominent notice with each copy of the object code that the
|
73
|
+
Library is used in it and that the Library and its use are
|
74
|
+
covered by this License.
|
75
|
+
|
76
|
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
77
|
+
document.
|
78
|
+
|
79
|
+
4. Combined Works.
|
80
|
+
|
81
|
+
You may convey a Combined Work under terms of your choice that,
|
82
|
+
taken together, effectively do not restrict modification of the
|
83
|
+
portions of the Library contained in the Combined Work and reverse
|
84
|
+
engineering for debugging such modifications, if you also do each of
|
85
|
+
the following:
|
86
|
+
|
87
|
+
a) Give prominent notice with each copy of the Combined Work that
|
88
|
+
the Library is used in it and that the Library and its use are
|
89
|
+
covered by this License.
|
90
|
+
|
91
|
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
92
|
+
document.
|
93
|
+
|
94
|
+
c) For a Combined Work that displays copyright notices during
|
95
|
+
execution, include the copyright notice for the Library among
|
96
|
+
these notices, as well as a reference directing the user to the
|
97
|
+
copies of the GNU GPL and this license document.
|
98
|
+
|
99
|
+
d) Do one of the following:
|
100
|
+
|
101
|
+
0) Convey the Minimal Corresponding Source under the terms of this
|
102
|
+
License, and the Corresponding Application Code in a form
|
103
|
+
suitable for, and under terms that permit, the user to
|
104
|
+
recombine or relink the Application with a modified version of
|
105
|
+
the Linked Version to produce a modified Combined Work, in the
|
106
|
+
manner specified by section 6 of the GNU GPL for conveying
|
107
|
+
Corresponding Source.
|
108
|
+
|
109
|
+
1) Use a suitable shared library mechanism for linking with the
|
110
|
+
Library. A suitable mechanism is one that (a) uses at run time
|
111
|
+
a copy of the Library already present on the user's computer
|
112
|
+
system, and (b) will operate properly with a modified version
|
113
|
+
of the Library that is interface-compatible with the Linked
|
114
|
+
Version.
|
115
|
+
|
116
|
+
e) Provide Installation Information, but only if you would otherwise
|
117
|
+
be required to provide such information under section 6 of the
|
118
|
+
GNU GPL, and only to the extent that such information is
|
119
|
+
necessary to install and execute a modified version of the
|
120
|
+
Combined Work produced by recombining or relinking the
|
121
|
+
Application with a modified version of the Linked Version. (If
|
122
|
+
you use option 4d0, the Installation Information must accompany
|
123
|
+
the Minimal Corresponding Source and Corresponding Application
|
124
|
+
Code. If you use option 4d1, you must provide the Installation
|
125
|
+
Information in the manner specified by section 6 of the GNU GPL
|
126
|
+
for conveying Corresponding Source.)
|
127
|
+
|
128
|
+
5. Combined Libraries.
|
129
|
+
|
130
|
+
You may place library facilities that are a work based on the
|
131
|
+
Library side by side in a single library together with other library
|
132
|
+
facilities that are not Applications and are not covered by this
|
133
|
+
License, and convey such a combined library under terms of your
|
134
|
+
choice, if you do both of the following:
|
135
|
+
|
136
|
+
a) Accompany the combined library with a copy of the same work based
|
137
|
+
on the Library, uncombined with any other library facilities,
|
138
|
+
conveyed under the terms of this License.
|
139
|
+
|
140
|
+
b) Give prominent notice with the combined library that part of it
|
141
|
+
is a work based on the Library, and explaining where to find the
|
142
|
+
accompanying uncombined form of the same work.
|
143
|
+
|
144
|
+
6. Revised Versions of the GNU Lesser General Public License.
|
145
|
+
|
146
|
+
The Free Software Foundation may publish revised and/or new versions
|
147
|
+
of the GNU Lesser General Public License from time to time. Such new
|
148
|
+
versions will be similar in spirit to the present version, but may
|
149
|
+
differ in detail to address new problems or concerns.
|
150
|
+
|
151
|
+
Each version is given a distinguishing version number. If the
|
152
|
+
Library as you received it specifies that a certain numbered version
|
153
|
+
of the GNU Lesser General Public License "or any later version"
|
154
|
+
applies to it, you have the option of following the terms and
|
155
|
+
conditions either of that published version or of any later version
|
156
|
+
published by the Free Software Foundation. If the Library as you
|
157
|
+
received it does not specify a version number of the GNU Lesser
|
158
|
+
General Public License, you may choose any version of the GNU Lesser
|
159
|
+
General Public License ever published by the Free Software Foundation.
|
160
|
+
|
161
|
+
If the Library as you received it specifies that a proxy can decide
|
162
|
+
whether future versions of the GNU Lesser General Public License shall
|
163
|
+
apply, that proxy's public statement of acceptance of any version is
|
164
|
+
permanent authorization for you to choose that version for the
|
165
|
+
Library.
|
166
|
+
|
data/ChangeLog
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
2008-08-11 Kenta Murata <muraken@gmail.com>
|
2
|
+
|
3
|
+
* Rakefile: replace all Spec::VERSION module to Measure::VERSION.
|
4
|
+
|
5
|
+
* lib/measure.rb (Measure.dim): add Measure.dim as alias to
|
6
|
+
Measure.dimension.
|
7
|
+
|
8
|
+
2008-08-11 Kenta Murata <mrkn@mrkn.jp>
|
9
|
+
|
10
|
+
* version 0.1.0
|
data/README
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= Measure library
|
2
|
+
|
3
|
+
Measure is a library to handle measurment numbers.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
The simplest approach is to install the gem (as root in some environments):
|
8
|
+
|
9
|
+
gem install -r measure
|
10
|
+
|
11
|
+
== Building the Measure gem
|
12
|
+
|
13
|
+
If you prefer to build the gem locally:
|
14
|
+
|
15
|
+
git clone git://github.com/mrkn/ruby-measure.git
|
16
|
+
cd ruby-measure
|
17
|
+
rake gem
|
18
|
+
gem install pkg/measure-0.x.x.gem # as root
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
$LOAD_PATH.unshift 'lib'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/contrib/rubyforgepublisher'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rake/testtask'
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
require 'measure/version'
|
10
|
+
dir = File.dirname(__FILE__)
|
11
|
+
|
12
|
+
PKG_NAME = 'measure'
|
13
|
+
PKG_VERSION = Measure::VERSION::STRING
|
14
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
15
|
+
PKG_FILES = FileList[
|
16
|
+
'[A-Z]*',
|
17
|
+
'lib/**/*.rb',
|
18
|
+
'spec/**/*',
|
19
|
+
]
|
20
|
+
|
21
|
+
task :default => [ :spec ]
|
22
|
+
|
23
|
+
desc 'Run all specs'
|
24
|
+
Spec::Rake::SpecTask.new do |t|
|
25
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
26
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'Run all specs and store html output in doc/output/report.html'
|
30
|
+
Spec::Rake::SpecTask.new('spec_html') do |t|
|
31
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
32
|
+
t.spec_opts = [ '--format html:../../../../doc/output/report.html',
|
33
|
+
'--format progress',
|
34
|
+
'--backtrace' ]
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'Generate RDoc'
|
38
|
+
rd = Rake::RDocTask.new do |rdoc|
|
39
|
+
rdoc.rdoc_dir = File.join('..', 'doc', 'output', 'rdoc')
|
40
|
+
rdoc.options << '--title' << 'Measure' << '--line-numbers'
|
41
|
+
rdoc.options << '--inline-source' << '--main' << 'README'
|
42
|
+
rdoc.rdoc_files.include('README', 'CHANGES', 'COPYING.LIB', 'lib/**/*.rb')
|
43
|
+
end
|
44
|
+
|
45
|
+
spec = Gem::Specification.new do |s|
|
46
|
+
s.name = PKG_NAME
|
47
|
+
s.version = PKG_VERSION
|
48
|
+
s.summary = Measure::VERSION::SUMMARY
|
49
|
+
s.description = <<-EOF
|
50
|
+
Measure is a library to handle measurement numbers.
|
51
|
+
EOF
|
52
|
+
|
53
|
+
s.files = PKG_FILES.to_a
|
54
|
+
s.require_path = 'lib'
|
55
|
+
|
56
|
+
s.has_rdoc = true
|
57
|
+
s.rdoc_options = rd.options
|
58
|
+
s.extra_rdoc_files = rd.rdoc_files.reject {|fn| fn =~ /\.rb$/ }.to_a
|
59
|
+
|
60
|
+
s.author = 'Kenta Murata'
|
61
|
+
s.email = 'measure-devel@rubyforge.org'
|
62
|
+
s.homepage = 'http://measure.rubyforge.org'
|
63
|
+
s.platform = Gem::Platform::RUBY
|
64
|
+
s.rubyforge_project = 'measure'
|
65
|
+
end
|
66
|
+
|
67
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
68
|
+
pkg.need_zip = true
|
69
|
+
pkg.need_tar = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def egrep(pattern)
|
73
|
+
Dir['**/*.rb'].each do |fn|
|
74
|
+
count = 0
|
75
|
+
open(fn) do |f|
|
76
|
+
while l = f.gets
|
77
|
+
count +=1
|
78
|
+
puts "#{fn}:#{count}:#{line}" if line =~ pattern
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'Look for TODO and FIXME tags in the code'
|
85
|
+
task :todo do
|
86
|
+
egrep /(FIXME|TODO)/
|
87
|
+
end
|
88
|
+
|
89
|
+
task :verify_user do
|
90
|
+
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'Publish gem+tgz+zip on RubyForge. You must make sure lib/version.rb is aligned with the ChangeLog file'
|
94
|
+
task :publish_packages => [ :verify_user, :package, :pkg ] do
|
95
|
+
release_files = FileList[
|
96
|
+
File.join('pkg', "#{PKG_FILE_NAME}.gem"),
|
97
|
+
File.join('pkg', "#{PKG_FILE_NAME}.zip"),
|
98
|
+
File.join('pkg', "#{PKG_FILE_NAME}.tgz"),
|
99
|
+
'CHANGES'
|
100
|
+
]
|
101
|
+
unless defined? Measure::VERSION::RELEASE_CANDIDATE and Measure::VERSION::RELEASE_CANDIDATE
|
102
|
+
require 'meta_project'
|
103
|
+
require 'rake/contrib/xforge'
|
104
|
+
|
105
|
+
project = MetaProject::Project::XForge::RubyForge.new(PKG_NAME)
|
106
|
+
Rake::XForge::Release.new(project) do |xf|
|
107
|
+
# Never hardcode user name and password in the Rakefile!
|
108
|
+
xf.user_name = ENV['RUBYFORGE_USER']
|
109
|
+
xf.files = release_files.to_a
|
110
|
+
xf.release_name = "Measure #{PKG_NAME}"
|
111
|
+
end
|
112
|
+
else
|
113
|
+
puts 'SINCE THIS IS A PRERELEASE, FILES ARE UPLOADED WITH SSH, NOT TO THE RUBYFORGE FILE SECTION'
|
114
|
+
puts 'YOU MUST TYPE THE PASSWORD #{release_files.length} TIMES...'
|
115
|
+
|
116
|
+
host = 'measure-website@rubyforge.org'
|
117
|
+
remote_dir = '/var/www/gforge-projects/#{PKG_NAME}'
|
118
|
+
|
119
|
+
publisher = Rake::SshFilePublisher.new(
|
120
|
+
host,
|
121
|
+
remote_dir,
|
122
|
+
File.dirname(__FILE__),
|
123
|
+
*release_files)
|
124
|
+
publisher.upload
|
125
|
+
|
126
|
+
puts 'UPLOADED THE FOLLOWING FILES:'
|
127
|
+
release_files.each do |fn|
|
128
|
+
name = File.basename(fn)
|
129
|
+
puts "* http://measure.rubyforge.org/#{name}"
|
130
|
+
end
|
131
|
+
|
132
|
+
puts "They are not linked to anywhere, so don't forget to tell people!"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc 'Publish news on RubyForge'
|
137
|
+
task :publish_news => [ :verify_user ] do
|
138
|
+
unless Measure::VERSION::RELEASE_CANDIDATE
|
139
|
+
require 'meta_project'
|
140
|
+
require 'rake/contrib/xforge'
|
141
|
+
|
142
|
+
project = MetaProject::Project::XForge::RubyForge.new(PKG_NAME)
|
143
|
+
Rake::XForge::NewsPublisher.new(project) do |news|
|
144
|
+
# Never hardcode user name and password in the Rakefile!
|
145
|
+
news.user_name = ENV['RUBYFORGE_USER']
|
146
|
+
end
|
147
|
+
else
|
148
|
+
puts '** Not publishing news to RubyForge because this is a prerelease'
|
149
|
+
end
|
150
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= Current version
|
2
|
+
|
3
|
+
- To write a spec for Measure#convert method with a unit same as receiver's one.
|
4
|
+
- To write specs for Measure#to_{a,i,f,s}.
|
5
|
+
- To write a spec for Measure#coerce.
|
6
|
+
- To write specs for binary operation.
|
7
|
+
- To write specs for comparison methods.
|
8
|
+
- To write RDoc documents.
|
9
|
+
|
10
|
+
= Future works
|
11
|
+
|
12
|
+
- To support composite dimension.
|
13
|
+
- To define well-known physical constants.
|
data/lib/measure.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
# = Measure
|
2
|
+
#
|
3
|
+
# Author:: Kenta Murata
|
4
|
+
# Copyright:: Copyright (C) 2008 Kenta Murata
|
5
|
+
# License:: LGPL version 3.0
|
6
|
+
|
7
|
+
class Measure
|
8
|
+
class UnitRedefinitionError < StandardError; end
|
9
|
+
class InvalidUnitError < StandardError; end
|
10
|
+
class CompatibilityError < StandardError; end
|
11
|
+
|
12
|
+
@@units = []
|
13
|
+
@@dimension_map = {}
|
14
|
+
@@conversion_map = {}
|
15
|
+
@@alias_map = {}
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def conversion_map
|
19
|
+
@@conversion_map
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_unit?(unit)
|
23
|
+
unit = resolve_alias unit
|
24
|
+
return @@units.include? unit
|
25
|
+
end
|
26
|
+
alias defined? has_unit?
|
27
|
+
|
28
|
+
def compatible?(u1, u2)
|
29
|
+
u1 = resolve_alias u1
|
30
|
+
raise InvalidUnitError, "unknown unit: #{u1}" unless self.defined? u1
|
31
|
+
u2 = resolve_alias u2
|
32
|
+
raise InvalidUnitError, "unknown unit: #{u2}" unless self.defined? u2
|
33
|
+
return true if u1 == u2
|
34
|
+
return true if @@conversion_map.has_key? u1 and @@conversion_map[u1].has_key? u2
|
35
|
+
return true if @@conversion_map.has_key? u2 and @@conversion_map[u2].has_key? u1
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear_units
|
40
|
+
@@units.clear
|
41
|
+
@@dimension_map.clear
|
42
|
+
@@conversion_map.clear
|
43
|
+
@@alias_map.clear
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def units(dimension=nil)
|
48
|
+
return @@units.dup if dimension.nil?
|
49
|
+
@@dimension_map.select {|k, v| v == dimension }.collect{|k, v| k }
|
50
|
+
end
|
51
|
+
|
52
|
+
def num_units
|
53
|
+
return @@units.length
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_unit(unit, dimension=1)
|
57
|
+
if @@units.include?(unit)
|
58
|
+
if self.dimension(unit) != dimension
|
59
|
+
raise UnitRedefinitionError, "unit [#{unit}] is already defined"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
@@units << unit
|
63
|
+
@@dimension_map[unit] = dimension
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_alias(unit, base)
|
68
|
+
if self.defined?(unit)
|
69
|
+
raise UnitRedefinitionError, "unit [#{unit}] is already defined"
|
70
|
+
end
|
71
|
+
raise InvalidUnitError, "unknown unit: #{base}" unless self.defined? base
|
72
|
+
@@alias_map[unit] = base
|
73
|
+
end
|
74
|
+
|
75
|
+
def define_conversion(base, conversion)
|
76
|
+
base = resolve_alias base
|
77
|
+
raise InvalidUnitError, "unknown unit: #{base}" unless self.defined? base
|
78
|
+
@@conversion_map[base] ||= {}
|
79
|
+
conversion.each {|unit, factor|
|
80
|
+
unit = resolve_alias unit
|
81
|
+
raise InvalidUnitError, "unknown unit: #{unit}" unless self.defined? unit
|
82
|
+
@@conversion_map[base][unit] = factor
|
83
|
+
}
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def dimension(unit)
|
88
|
+
return @@dimension_map[resolve_alias unit]
|
89
|
+
end
|
90
|
+
alias dim dimension
|
91
|
+
|
92
|
+
def resolve_alias(unit)
|
93
|
+
while @@alias_map.has_key? unit
|
94
|
+
unit = @@alias_map[unit]
|
95
|
+
end
|
96
|
+
return unit
|
97
|
+
end
|
98
|
+
|
99
|
+
def find_multi_hop_conversion(u1, u2)
|
100
|
+
visited = []
|
101
|
+
queue = [[u1]]
|
102
|
+
while route = queue.shift
|
103
|
+
next if visited.include? route.last
|
104
|
+
visited.push route.last
|
105
|
+
return route if route.last == u2
|
106
|
+
neighbors(route.last).each{|u|
|
107
|
+
queue.push(route + [u]) unless visited.include? u }
|
108
|
+
end
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
|
112
|
+
# def encode_dimension(dim)
|
113
|
+
# case dim
|
114
|
+
# when Symbol
|
115
|
+
# return dim.to_s
|
116
|
+
# else
|
117
|
+
# units = dim.sort {|a, b| a[1] <=> b[1] }.reverse
|
118
|
+
# nums = []
|
119
|
+
# units.select {|u, e| e > 0 }.each {|u, e| nums << "#{u}^#{e}" }
|
120
|
+
# dens = []
|
121
|
+
# units.select {|u, e| e < 0 }.each {|u, e| dens << "#{u}^#{-e}" }
|
122
|
+
# return nums.join(' ') + ' / ' + dens.join(' ')
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def neighbors(unit)
|
129
|
+
res = []
|
130
|
+
if @@conversion_map.has_key?(unit)
|
131
|
+
res += @@conversion_map[unit].keys
|
132
|
+
end
|
133
|
+
@@conversion_map.each {|k, v| res << k if v.has_key? unit }
|
134
|
+
return res
|
135
|
+
end
|
136
|
+
end # class << self
|
137
|
+
|
138
|
+
def initialize(value, unit)
|
139
|
+
@value, @unit = value, unit
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
|
143
|
+
attr_reader :value, :unit
|
144
|
+
|
145
|
+
def <(other)
|
146
|
+
case other
|
147
|
+
when Measure
|
148
|
+
if self.unit == other.unit
|
149
|
+
return self.value < other.value
|
150
|
+
else
|
151
|
+
return self < other.convert(self.value)
|
152
|
+
end
|
153
|
+
when Numeric
|
154
|
+
return self.value < other
|
155
|
+
else
|
156
|
+
raise ArgumentError, 'unable to compare with #{other.inspect}'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def >(other)
|
161
|
+
case other
|
162
|
+
when Measure
|
163
|
+
if self.unit == other.unit
|
164
|
+
return self.value > other.value
|
165
|
+
else
|
166
|
+
return self > other.convert(self.value)
|
167
|
+
end
|
168
|
+
when Numeric
|
169
|
+
return self.value > other
|
170
|
+
else
|
171
|
+
raise ArgumentError, 'unable to compare with #{other.inspect}'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def ==(other)
|
176
|
+
return self.value == other.value if self.unit == other.unit
|
177
|
+
if Measure.compatible? self.unit, other.unit
|
178
|
+
return self == other.convert(self.unit)
|
179
|
+
elsif Measure.compatible? other.unit, self.unit
|
180
|
+
return self.convert(other.unit) == other
|
181
|
+
else
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def +(other)
|
187
|
+
case other
|
188
|
+
when Measure
|
189
|
+
if self.unit == other.unit
|
190
|
+
return Measure(self.value + other.value, self.unit)
|
191
|
+
elsif Measure.dim(self.unit) == Measure.dim(other.unit)
|
192
|
+
return Measure(self.value + other.convert(self.unit).value, self.unit)
|
193
|
+
else
|
194
|
+
raise TypeError, "incompatible dimensions: " +
|
195
|
+
"#{Measure.dim(self.unit)} and #{Measure.dim(other.unit)}"
|
196
|
+
end
|
197
|
+
when Numeric
|
198
|
+
return Measure(self.value + other, self.unit)
|
199
|
+
else
|
200
|
+
check_coercable other
|
201
|
+
a, b = other.coerce self
|
202
|
+
return a + b
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def -(other)
|
207
|
+
case other
|
208
|
+
when Measure
|
209
|
+
if self.unit == other.unit
|
210
|
+
return Measure(self.value - other.value, self.unit)
|
211
|
+
elsif Measure.dim(self.unit) == Measure.dim(other.unit)
|
212
|
+
return Measure(self.value - other.convert(self.unit).value, self.unit)
|
213
|
+
else
|
214
|
+
raise TypeError, "incompatible dimensions: " +
|
215
|
+
"#{Measure.dim(self.unit)} and #{Measure.dim(other.unit)}"
|
216
|
+
end
|
217
|
+
when Numeric
|
218
|
+
return Measure(self.value - other, self.unit)
|
219
|
+
else
|
220
|
+
check_coerecable other
|
221
|
+
a, b = other.coerce self
|
222
|
+
return a - b
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def *(other)
|
227
|
+
case other
|
228
|
+
when Measure
|
229
|
+
# TODO: dimension
|
230
|
+
return other * self.value if self.unit == 1
|
231
|
+
raise NotImplementedError, "this feature has not implemented yet"
|
232
|
+
# if self.unit == other.unit
|
233
|
+
# return Measure(self.value * other.value, self.unit)
|
234
|
+
# elsif Measure.dim(self.unit) == Measure.dim(other.unit)
|
235
|
+
# return Measure(self.value - other.convert(self.unit).value, self.unit)
|
236
|
+
# else
|
237
|
+
# return Measure(self.value * other.convert(self.unit).value, self.unit)
|
238
|
+
# end
|
239
|
+
when Numeric
|
240
|
+
return Measure(self.value * other, self.unit)
|
241
|
+
else
|
242
|
+
check_coercable other
|
243
|
+
a, b = other.coerce self
|
244
|
+
return a * b
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def /(other)
|
249
|
+
case other
|
250
|
+
when Measure
|
251
|
+
# TODO: dimension
|
252
|
+
raise NotImplementedError, "this feature has not implemented yet"
|
253
|
+
# if self.unit == other.unit
|
254
|
+
# return Measure(self.value / other.value, self.unit)
|
255
|
+
# else
|
256
|
+
# return Measure(self.value / other.convert(self.unit).value, self.unit)
|
257
|
+
# end
|
258
|
+
when Numeric
|
259
|
+
return Measure(self.value / other, self.unit)
|
260
|
+
else
|
261
|
+
check_coercable other
|
262
|
+
a, b = other.coerce self
|
263
|
+
return a / b
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def coerce(other)
|
268
|
+
case other
|
269
|
+
when Numeric
|
270
|
+
return [Measure(other, 1), self]
|
271
|
+
else
|
272
|
+
raise TypeError, "#{other.class} can't convert into #{self.class}"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def abs
|
277
|
+
return Measure(self.value.abs, self.unit)
|
278
|
+
end
|
279
|
+
|
280
|
+
def to_s
|
281
|
+
return "#{self.value} [#{self.unit}]"
|
282
|
+
end
|
283
|
+
|
284
|
+
def to_i
|
285
|
+
return self.value.to_i
|
286
|
+
end
|
287
|
+
|
288
|
+
def to_f
|
289
|
+
return self.value.to_f
|
290
|
+
end
|
291
|
+
|
292
|
+
def to_a
|
293
|
+
return [self.value, self.unit]
|
294
|
+
end
|
295
|
+
|
296
|
+
def convert(unit)
|
297
|
+
return self if unit == self.unit
|
298
|
+
to_unit = Measure.resolve_alias unit
|
299
|
+
raise InvalidUnitError, "unknown unit: #{unit}" unless Measure.defined? unit
|
300
|
+
from_unit = Measure.resolve_alias self.unit
|
301
|
+
if Measure.compatible? from_unit, to_unit
|
302
|
+
# direct conversion
|
303
|
+
if @@conversion_map.has_key? from_unit and @@conversion_map[from_unit].has_key? to_unit
|
304
|
+
value = self.value * @@conversion_map[from_unit][to_unit]
|
305
|
+
else
|
306
|
+
value = self.value / @@conversion_map[to_unit][from_unit].to_f
|
307
|
+
end
|
308
|
+
elsif route = Measure.find_multi_hop_conversion(from_unit, to_unit)
|
309
|
+
u1 = route.shift
|
310
|
+
value = self.value
|
311
|
+
while u2 = route.shift
|
312
|
+
if @@conversion_map.has_key? u1 and @@conversion_map[u1].has_key? u2
|
313
|
+
value *= @@conversion_map[u1][u2]
|
314
|
+
else
|
315
|
+
value /= @@conversion_map[u2][u1].to_f
|
316
|
+
end
|
317
|
+
u1 = u2
|
318
|
+
end
|
319
|
+
else
|
320
|
+
raise CompatibilityError, "units not compatible: #{self.unit} and #{unit}"
|
321
|
+
end
|
322
|
+
# Second
|
323
|
+
return Measure.new(value, unit)
|
324
|
+
end
|
325
|
+
|
326
|
+
alias saved_method_missing method_missing
|
327
|
+
private_methods :saved_method_missing
|
328
|
+
|
329
|
+
def method_missing(name, *args)
|
330
|
+
if /^as_(\w+)/.match(name.to_s)
|
331
|
+
unit = $1.to_sym
|
332
|
+
return convert(unit)
|
333
|
+
end
|
334
|
+
return saved_method_missing(name, *args)
|
335
|
+
end
|
336
|
+
|
337
|
+
private
|
338
|
+
|
339
|
+
def check_coercable(other)
|
340
|
+
unless other.respond_to? :coerce
|
341
|
+
raise TypeError, "#{other.class} can't be coerced into #{self.class}"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def Measure(value, unit=1)
|
347
|
+
return Measure.new(value, unit)
|
348
|
+
end
|