measurb 0.0.1
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/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +130 -0
- data/Rakefile +1 -0
- data/lib/measurb/config.rb +32 -0
- data/lib/measurb/core_ext.rb +33 -0
- data/lib/measurb/defaults/feet.rb +4 -0
- data/lib/measurb/defaults/inches.rb +4 -0
- data/lib/measurb/defaults/yards.rb +4 -0
- data/lib/measurb/dimension.rb +93 -0
- data/lib/measurb/dimension_builder.rb +108 -0
- data/lib/measurb/version.rb +3 -0
- data/lib/measurb.rb +109 -0
- data/measurb.gemspec +25 -0
- data/spec/arithmetic_spec.rb +67 -0
- data/spec/comparison_spec.rb +61 -0
- data/spec/config_spec.rb +90 -0
- data/spec/conversions_spec.rb +52 -0
- data/spec/define_dimension_spec.rb +54 -0
- data/spec/dimension_builder_spec.rb +19 -0
- data/spec/dimension_precision_spec.rb +83 -0
- data/spec/equality_spec.rb +70 -0
- data/spec/helpers/clear_dimensions.rb +26 -0
- data/spec/helpers/matchers.rb +17 -0
- data/spec/numeric_ext_spec.rb +41 -0
- data/spec/spec_helper.rb +77 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 041ff65242c85675992b2b42b67f1747c24f8e64
|
4
|
+
data.tar.gz: 37ec1b92574b3a80ee26999d295baf35824ebb7d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c3539e48fb3b4bbe21e341d56907fa7bd4526ccebf61c5e4fe4379c3e9cd22c2c2b7f324c6c0677d74922d83978d112e6f533b0d50e8a811846c498d2b0bc2ca
|
7
|
+
data.tar.gz: e287a287a4f9bab984ff8f8addaeb8b4df0d75551985dd20c1e7c6230bbcac52a34800db7f3def1d039542c067a5c2ecccf13ffeee82c5f59e508dab95efb40a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jeremy Fairbank
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Measurb
|
2
|
+
|
3
|
+
Handle units of measurment with ease! Measurb is a Ruby library for creating and managing units of measurements, or dimensions. Create your own units and define how to convert amongst them. Measurb also comes with a few default dimensions: inches, feet, and yards, but you don't have to use them. Measurb takes care of the tediousness of adding, subtracting, and comparing different measurements, especially between different units of measurement.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'measurb'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install measurb
|
18
|
+
|
19
|
+
## Defining Dimensions
|
20
|
+
|
21
|
+
Measurb has a straightforward interface to defining dimensions. Just give `Measurb.define` a dimension name and optional block to define conversions.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# Without conversions
|
25
|
+
Measurb.define :inches
|
26
|
+
|
27
|
+
# With conversion
|
28
|
+
Measurb.define :inches do
|
29
|
+
feet value / 12.0
|
30
|
+
yards value / 36.0
|
31
|
+
end
|
32
|
+
|
33
|
+
Measurb.define :feet do
|
34
|
+
inches value * 12.0
|
35
|
+
yards value / 3.0
|
36
|
+
end
|
37
|
+
|
38
|
+
Measurb.define :yards do
|
39
|
+
feet value * 3.0
|
40
|
+
inches value * 36.0
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
You can specify an abbreviation for a dimension too.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
Measurb.define :ft, abbrev: 'ft'
|
48
|
+
```
|
49
|
+
|
50
|
+
## Using Dimensions
|
51
|
+
|
52
|
+
When you define a dimension, Measurb creates a dimension class that can be instantiated.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
Measurb.define :inches
|
56
|
+
forty_two_inches = Measurb::Inches.new(42)
|
57
|
+
```
|
58
|
+
|
59
|
+
Merb also defines a singleton method on itself and a method on `Numeric`, both of the same dimension name.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
two_inches = Measurb.inches(2) # singleton method
|
63
|
+
three_inches = 3.inches # Numeric method
|
64
|
+
```
|
65
|
+
|
66
|
+
If you defined an abbreviation, you can use it on `Numeric` as well.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
Measurb.define :ft, abbrev: 'ft'
|
70
|
+
5.feet == 5.ft #=> true
|
71
|
+
```
|
72
|
+
|
73
|
+
## Converting
|
74
|
+
|
75
|
+
To convert a dimension, you must have set up at least two dimensions, including the appropriate conversion formulas.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Measurb.define :inches do
|
79
|
+
feet value / 12.0
|
80
|
+
end
|
81
|
+
|
82
|
+
Measurb.define :feet do
|
83
|
+
inches value * 12.0
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
For every conversion you set up in the definition block, Measurb will add a `to_*` method for that conversion.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
24.inches.to_feet == 2.feet
|
91
|
+
3.feet.to_inches == 36.inches
|
92
|
+
```
|
93
|
+
|
94
|
+
## Arithmetic
|
95
|
+
|
96
|
+
Measurb supports addition and subtraction currently. You can perform operations amongst all the defined dimension classes. Make sure you've define your conversions beforehand, though! The resulting value will use the class of the leftmost operand.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
2.feet + 12.inches == 3.feet
|
100
|
+
3.feet - 6.inches == 2.5.feet
|
101
|
+
3.yards - 1.feet == 2.yards
|
102
|
+
```
|
103
|
+
|
104
|
+
## Equality and Comparisons
|
105
|
+
|
106
|
+
Measurb also supports all the typical equality and inequality checks `==` `!=` `<` `>` `<=` `>=` `<=>`. They work correctly with other dimension classes too.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
42.feet == 42.feet #=> true
|
110
|
+
2.feet < 26.inches #=> true
|
111
|
+
3.feet > 2.yards #=> false
|
112
|
+
24.inches <= 2.feet #=> true
|
113
|
+
5.feet <=> 2.yards #=> -1
|
114
|
+
24.inches != 2.feet #=> false
|
115
|
+
```
|
116
|
+
|
117
|
+
`eql?` is also implemented, but it DOES depend on the same dimension class to be true
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
2.feet.eql? 2.feet #=> true
|
121
|
+
2.feet.eql? 24.inches #=> false
|
122
|
+
```
|
123
|
+
|
124
|
+
## Contributing
|
125
|
+
|
126
|
+
1. Fork it ( http://github.com/jfairbank/measurb/fork )
|
127
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
128
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
129
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
130
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Measurb
|
2
|
+
# Class for managing configuration of {Measurb}
|
3
|
+
class Config
|
4
|
+
# Enable defaults dimensions based on their name
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Measurb.configure do |config|
|
10
|
+
# config.enable_defaults :inches, :feet
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @param names [Symbol, String] The dimension names to enable
|
14
|
+
# @return [Array<Symbol, String>] The dimension names that were enabled
|
15
|
+
def enable_defaults(*names)
|
16
|
+
enabled = []
|
17
|
+
|
18
|
+
names.each do |name|
|
19
|
+
path = "measurb/defaults/#{name}"
|
20
|
+
|
21
|
+
begin
|
22
|
+
require path
|
23
|
+
enabled << name
|
24
|
+
rescue LoadError
|
25
|
+
warn "'#{name}' is not a default dimension"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
enabled
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Measurb
|
2
|
+
# Module for extending core classes
|
3
|
+
module CoreExt
|
4
|
+
# Add the dimension name method to `Numeric`
|
5
|
+
#
|
6
|
+
# @api semipublic
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# 42.respond_to?(:inches) #=> false
|
10
|
+
#
|
11
|
+
# Measurb::CoreExt.add_numeric_dimension(:inches, 'Inches', 'in')
|
12
|
+
#
|
13
|
+
# 42.respond_to?(:inches) #=> true
|
14
|
+
#
|
15
|
+
# @param name [Symbol, String] Name of the dimension
|
16
|
+
# @param dimension_class_name [String] Name of the dimension class
|
17
|
+
# @param abbrev [String] Abbreviation for the dimension name
|
18
|
+
# @return [nil]
|
19
|
+
def self.add_numeric_dimension(name, dimension_class_name, abbrev)
|
20
|
+
def_string = <<-EOS
|
21
|
+
def #{name}(precision = #{DEFAULT_PRECISION})
|
22
|
+
Measurb::#{dimension_class_name}.new(self, precision)
|
23
|
+
end
|
24
|
+
EOS
|
25
|
+
|
26
|
+
unless abbrev.nil?
|
27
|
+
def_string << "\nalias_method :#{abbrev}, :#{name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
Numeric.class_eval(def_string)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Measurb
|
2
|
+
# Base class for defined dimensions to inherit from.
|
3
|
+
class Dimension
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :value, :precision
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_reader :abbrev, :dimension_name
|
10
|
+
end
|
11
|
+
|
12
|
+
# Initialize the dimension
|
13
|
+
#
|
14
|
+
# @param value [Numeric] Numeric value to wrap
|
15
|
+
# @param precision [Integer] Precision of decimal places
|
16
|
+
# @return [Measurb::Dimension]
|
17
|
+
def initialize(value, precision = DEFAULT_PRECISION)
|
18
|
+
@precision = precision
|
19
|
+
@original_value = value
|
20
|
+
@value = fix_value(value, precision)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add another dimension
|
24
|
+
#
|
25
|
+
# @param other [Measurb::Dimension]
|
26
|
+
# @return [Measurb::Dimension]
|
27
|
+
def +(other)
|
28
|
+
arithmetic(:+, other)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Subtract another dimension
|
32
|
+
#
|
33
|
+
# @param other [Measurb::Dimension]
|
34
|
+
# @return [Measurb::Dimension]
|
35
|
+
def -(other)
|
36
|
+
arithmetic(:-, other)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Compare with another dimension
|
40
|
+
#
|
41
|
+
# @param other [Measurb::Dimension]
|
42
|
+
# @return [-1, 0, 1]
|
43
|
+
def <=>(other)
|
44
|
+
value <=> to_self(other).value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Check type and value quality with another dimension
|
48
|
+
#
|
49
|
+
# @param other [Measurb::Dimension]
|
50
|
+
# @return [Boolean]
|
51
|
+
def eql?(other)
|
52
|
+
self.class == other.class && self == other
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get the inspect string
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
def inspect
|
59
|
+
"#{value} #{self.class.abbrev || self.class.dimension_name}"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Perform an arithmetic operation `name` with `other`, keeping the
|
65
|
+
# smallest precision of the two dimensions.
|
66
|
+
#
|
67
|
+
# @param op [Symbol] Name of the arithmetic operation
|
68
|
+
# @param other [Measurb::Dimension]
|
69
|
+
# @return [Measurb::Dimension]
|
70
|
+
def arithmetic(op, other)
|
71
|
+
least_precision = [precision, other.precision].min
|
72
|
+
new_value = value.__send__(op, to_self(other).value)
|
73
|
+
self.class.new(new_value, least_precision)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Coerce other dimension to own class
|
77
|
+
#
|
78
|
+
# @param other [Measurb::Dimension]
|
79
|
+
# @return [self.class]
|
80
|
+
def to_self(other)
|
81
|
+
other.__send__("to_#{self.class.dimension_name}")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Adjust a value to a given decimal precision
|
85
|
+
#
|
86
|
+
# @param value [Numeric]
|
87
|
+
# @param precision [Integer] Precision of decimal places
|
88
|
+
def fix_value(value, precision)
|
89
|
+
modifier = (10 ** precision).to_f
|
90
|
+
(value * modifier).round / modifier
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Measurb
|
2
|
+
# Factory class for building new dimension classes
|
3
|
+
class DimensionBuilder
|
4
|
+
attr_reader :dimension_class_name, :value
|
5
|
+
|
6
|
+
# Get an appropriate class name for a dimension name
|
7
|
+
#
|
8
|
+
# @param name [Symbol, String] Name of the dimension
|
9
|
+
# @return [String] Name of the dimension class
|
10
|
+
def self.get_dimension_class_name(name)
|
11
|
+
name.to_s.split('_').map(&:capitalize).join
|
12
|
+
end
|
13
|
+
|
14
|
+
# Initialize the builder
|
15
|
+
#
|
16
|
+
# @param name [Symbol, String] Name of the dimension
|
17
|
+
# @param options [Hash]
|
18
|
+
# @option options [String] :abbrev (nil) The dimension abbreviation
|
19
|
+
# @param block [Proc] Block for defining conversions to other dimensions
|
20
|
+
def initialize(name, options = {}, &block)
|
21
|
+
@name = name
|
22
|
+
@def_block = block
|
23
|
+
@options = options
|
24
|
+
@dimension_class_name = self.class.get_dimension_class_name(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @!attribute [r] dimension_class
|
28
|
+
# Get the dimension class, creating it if it's not available
|
29
|
+
# @return [Measurb::Dimension]
|
30
|
+
def dimension_class
|
31
|
+
build_dimension_class unless defined?(@dimension_class)
|
32
|
+
@dimension_class
|
33
|
+
end
|
34
|
+
|
35
|
+
# Use to build conversion methods to other dimensions
|
36
|
+
def method_missing(name, *args, &block)
|
37
|
+
build_default_conversion(name, args.first)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Build a conversion to another dimension with a default implementation
|
43
|
+
#
|
44
|
+
# @param name [Symbol, String] Name of the dimension
|
45
|
+
# @param conversion [Proc] Proc to convert the actual value
|
46
|
+
def build_default_conversion(name, conversion)
|
47
|
+
class_name = self.class.get_dimension_class_name(name)
|
48
|
+
|
49
|
+
build_conversion(name) do
|
50
|
+
precision ||= self.precision
|
51
|
+
Measurb.const_get(class_name).new(instance_eval(&conversion), precision)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Build a conversion method, using `block` as the body
|
56
|
+
#
|
57
|
+
# @param name [Symbol, String] Name of the dimension
|
58
|
+
# @param block [Proc] Conversion method body
|
59
|
+
def build_conversion(name, &block)
|
60
|
+
@dimension_class.__send__(:define_method, "to_#{name}", &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Build the dimension class, defining any conversions from `@def_block` and
|
64
|
+
# defining a default conversion to itself
|
65
|
+
def build_dimension_class
|
66
|
+
@value = ValueProxy.new
|
67
|
+
@dimension_class = Class.new(Dimension)
|
68
|
+
|
69
|
+
@dimension_class.instance_variable_set(:@dimension_name, @name.to_s)
|
70
|
+
@dimension_class.instance_variable_set(:@abbrev, @options[:abbrev])
|
71
|
+
|
72
|
+
instance_eval(&@def_block) unless @def_block.nil?
|
73
|
+
|
74
|
+
# Return self when converting to same dimension
|
75
|
+
build_conversion(@name) { self }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Placeholder class in definition blocks for writing straightforward conversion syntax.
|
79
|
+
# Currently only supports multiplication and division.
|
80
|
+
#
|
81
|
+
# ==== Examples
|
82
|
+
#
|
83
|
+
# Measurb.define :inches do
|
84
|
+
# feet value / 12.0
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# Measurb.define :feet do
|
88
|
+
# inches value * 12.0
|
89
|
+
# end
|
90
|
+
class ValueProxy
|
91
|
+
# Handle a conversion by multiplication
|
92
|
+
#
|
93
|
+
# @param convert_value [Float]
|
94
|
+
# @return [Proc] Proc to be used as the body for the conversion method on the {Dimension dimension class}
|
95
|
+
def *(convert_value)
|
96
|
+
proc { value * convert_value }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Handle a conversion by division
|
100
|
+
#
|
101
|
+
# @param convert_value [Float]
|
102
|
+
# @return [Proc] Proc to be used as the body for the conversion method on the {Dimension dimension class}
|
103
|
+
def /(convert_value)
|
104
|
+
proc { value / convert_value }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/measurb.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'measurb/version'
|
2
|
+
require 'measurb/config'
|
3
|
+
require 'measurb/core_ext'
|
4
|
+
require 'measurb/dimension'
|
5
|
+
require 'measurb/dimension_builder'
|
6
|
+
|
7
|
+
# Main module
|
8
|
+
module Measurb
|
9
|
+
DEFAULT_PRECISION = 3
|
10
|
+
|
11
|
+
# Error when attempting to define a dimension that already exists
|
12
|
+
class DuplicateDimensionError < NameError; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Define a new dimension class and helper singleton method
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
#
|
19
|
+
# @example Without conversions
|
20
|
+
# Measurb.define :inches
|
21
|
+
#
|
22
|
+
# @example With conversions
|
23
|
+
# Measurb.define :inches do
|
24
|
+
# feet value / 12.0
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Measurb.define :feet do
|
28
|
+
# inches value * 12.0
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @param name [Symbol, String] Name of the dimension
|
32
|
+
# @return [Measurb::Dimension] A subclass of {Measurb::Dimension}
|
33
|
+
def define(name, options = {}, &block)
|
34
|
+
# Check if the dimension has already been defined
|
35
|
+
if dimension_exists?(name)
|
36
|
+
raise DuplicateDimensionError,
|
37
|
+
"Already defined dimension class `#{DimensionBuilder.get_dimension_class_name(name)}`"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create the dimension builder
|
41
|
+
builder = DimensionBuilder.new(name, options, &block)
|
42
|
+
|
43
|
+
# Add the class constant and helper method to the module
|
44
|
+
const_set(builder.dimension_class_name, builder.dimension_class)
|
45
|
+
add_dimension_method(name, builder.dimension_class_name)
|
46
|
+
|
47
|
+
# Add the helper methods to 'Numeric'
|
48
|
+
CoreExt.add_numeric_dimension(name, builder.dimension_class_name, builder.dimension_class.abbrev)
|
49
|
+
|
50
|
+
builder.dimension_class
|
51
|
+
end
|
52
|
+
|
53
|
+
# Configure {Measurb} with {Config}
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# Measurb.configure do |config|
|
59
|
+
# config.enable_defaults :inches, :feet
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @return [nil]
|
63
|
+
def configure
|
64
|
+
yield config
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if the dimension already exists
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# Measurb.configure { |config| config.enable_defaults(:inches) }
|
73
|
+
#
|
74
|
+
# Measurb.dimension_exists?(:inches) #=> true
|
75
|
+
# Measurb.dimension_exists?(:feet) #=> false
|
76
|
+
#
|
77
|
+
# @param name [Symbol, String] Name of the dimension
|
78
|
+
# @return [Boolean]
|
79
|
+
def dimension_exists?(name)
|
80
|
+
const_defined?(DimensionBuilder.get_dimension_class_name(name))
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Config object used by {#configure}
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
#
|
89
|
+
# @return [Config] The configurable object
|
90
|
+
def config
|
91
|
+
@config ||= Config.new
|
92
|
+
end
|
93
|
+
|
94
|
+
# Create the dimension class singleton method
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
#
|
98
|
+
# @param name [Symbol, String] Name of the dimension
|
99
|
+
# @param dimension_class_name [String] Name of the dimension class
|
100
|
+
# @return [Symbol, nil] Return type based on ruby version
|
101
|
+
def add_dimension_method(name, dimension_class_name)
|
102
|
+
instance_eval <<-EOS, __FILE__, __LINE__ + 1
|
103
|
+
def #{name}(value, precision = DEFAULT_PRECISION)
|
104
|
+
#{dimension_class_name}.new(value, precision)
|
105
|
+
end
|
106
|
+
EOS
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/measurb.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'measurb/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'measurb'
|
8
|
+
spec.version = Measurb::VERSION
|
9
|
+
spec.authors = ['Jeremy Fairbank']
|
10
|
+
spec.email = ['elpapapollo@gmail.com']
|
11
|
+
spec.summary = %q{Handle units of measurement}
|
12
|
+
spec.description = %q{Handle units of measurement}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
#spec.files = `git ls-files -z`.split('\x0')
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Measurb::Dimension do
|
4
|
+
before :context do
|
5
|
+
clear_defined_inches
|
6
|
+
clear_defined_feet
|
7
|
+
clear_defined_yards
|
8
|
+
remove_loaded_default_dimensions
|
9
|
+
|
10
|
+
Measurb.configure do |config|
|
11
|
+
config.enable_defaults :inches, :feet
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:inches) { 24.inches }
|
16
|
+
let(:feet) { 5.feet }
|
17
|
+
|
18
|
+
context 'when adding' do
|
19
|
+
it 'returns the left hand side class' do
|
20
|
+
expect(inches + inches).to be_a(Measurb::Inches)
|
21
|
+
expect(inches + feet).to be_a(Measurb::Inches)
|
22
|
+
expect(feet + feet).to be_a(Measurb::Feet)
|
23
|
+
expect(feet + inches).to be_a(Measurb::Feet)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'adds correctly' do
|
27
|
+
expect(inches + inches).to have_value(48)
|
28
|
+
expect(inches + feet).to have_value(84)
|
29
|
+
expect(feet + feet).to have_value(10)
|
30
|
+
expect(feet + inches).to have_value(7)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'respects the smallest precision' do
|
34
|
+
x = Math::PI.inches(4)
|
35
|
+
y = 2.001.inches(3)
|
36
|
+
sum = x + y
|
37
|
+
|
38
|
+
expect(sum).to have_precision(3)
|
39
|
+
expect(sum).to have_value(5.143)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when subtracting' do
|
44
|
+
it 'returns the left hand side class' do
|
45
|
+
expect(inches - inches).to be_a(Measurb::Inches)
|
46
|
+
expect(inches - feet).to be_a(Measurb::Inches)
|
47
|
+
expect(feet - feet).to be_a(Measurb::Feet)
|
48
|
+
expect(feet - inches).to be_a(Measurb::Feet)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'subtracts correctly' do
|
52
|
+
expect(inches - inches).to have_value(0)
|
53
|
+
expect(inches - feet).to have_value(-36)
|
54
|
+
expect(feet - feet).to have_value(0)
|
55
|
+
expect(feet - inches).to have_value(3)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'respects the smallest precision' do
|
59
|
+
x = Math::PI.inches(4)
|
60
|
+
y = 2.001.inches(3)
|
61
|
+
difference = x - y
|
62
|
+
|
63
|
+
expect(difference).to have_precision(3)
|
64
|
+
expect(difference).to have_value(1.141)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|