measurements 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ .yardoc
3
+ doc/*
4
+ .DS_Store*
5
+ gems/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --no-private
2
+ --protected
3
+ --markup markdown
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,19 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+ # Capybara request specs
17
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
18
+ end
19
+
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # Measurements Ruby Gem
2
+ This gem was created to assist in converting cooking measurements when scaling up a recipe.
3
+ I was having fun with it so I decided to go a little above and beyond.
4
+
5
+ **Note:** This project ended up getting bigger than I had expected and now there are quite a few things that need fixing.
6
+ I should have had a plan from the beginning...
7
+
8
+ ## Using the Gem
9
+
10
+ Install it with
11
+
12
+ ```shell
13
+ gem install measurements
14
+ ```
15
+
16
+ and enjoy.
17
+
18
+ ## Documentation
19
+ ```shell
20
+ yardoc
21
+ ```
22
+
23
+ ## Usage Examples
24
+ Create an Ounce object with a quantity of 3oz
25
+
26
+ ```ruby
27
+ ounce = Measurements.new_unit :ounce, 3
28
+ ```
29
+
30
+ Convert that 3oz object to a Pound object
31
+
32
+ ```ruby
33
+ pound = ounce.convert_to :pound
34
+ ```
35
+
36
+ Convert the current unit by altering the unit itself
37
+
38
+ ```ruby
39
+ ounce.convert_to! :pound
40
+ ```
41
+
42
+ Create an Ounce object with a type of solid. This would be used to make sure when you convert
43
+ using the smart convert (not yet implemented) the unit doesn't get converted to a fluid measurement
44
+
45
+ ```ruby
46
+ ounce = Measurements.new_unit :ounce, 3.5, :solid
47
+ ```
48
+
49
+ Get the list of available units
50
+
51
+ ```ruby
52
+ Measurements.available_units
53
+ => [:ounce, :pound, :teaspoon, :tablespoon, :cup, :pint, :quart, :gallon, :inch, :foot, :yard, :furlong, :chain, :mile, :thou, :league]
54
+ ```
55
+
56
+ Get the list of available types of units. ex: Solid, Fluid or Neutral
57
+
58
+ ```ruby
59
+ Measurements.available_types
60
+ => [:solid, :fluid, :neutral, :base]
61
+ ```
62
+
63
+ ## Getting Started with Development
64
+ Make sure you have rubygems installed. You can find instructions at [rubygems.org](http://docs.rubygems.org/read/chapter/3)
65
+
66
+ Have bundler installed on your system
67
+
68
+ gem install bundler
69
+
70
+ Run bundler to make sure you have all the right gems
71
+
72
+ bundle install
73
+
74
+ Generate some docs to browse while I work on getting some tests ready. After running the following command
75
+ browse to the doc directory and open the index.html file in your favorite browser.
76
+
77
+ yardoc
78
+
79
+ Right now when I've been testing the gem inside irb I've been running the ruby line.
80
+
81
+ ```ruby
82
+ $LOAD_PATH.unshift(File.dirname(__FILE__)+"/lib"); require 'measurements'
83
+ ```
84
+
85
+ You should also note that this only works if you launch irb from within the measurements directory.
86
+
87
+ If you want to be snazy and grab this line to load without using a text editor you can run one of these commands
88
+
89
+ ##### Unix
90
+
91
+ ```shell
92
+ cat README.md | grep "LOAD_PATH"
93
+ ```
94
+
95
+ ##### Windows
96
+
97
+ ```shell
98
+ type README.md | find "LOAD_PATH"
99
+ ```
100
+
101
+ ## Copyright
102
+ Copyright (c) 2012 Casey Stehlik.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ desc "Run tests"
7
+ task :test => :spec
8
+
9
+ task :default => :spec
@@ -0,0 +1,19 @@
1
+ # Always make the version available
2
+ require 'measurements/version'
3
+
4
+ # @author Casey Stehlik
5
+ module Measurements
6
+
7
+ # This is the local to use when looking up abbreviations. Later on you will be able
8
+ # to select other locales, but for now you get only english
9
+ LOCALE = "en"
10
+
11
+ autoload :Type, 'measurements/type'
12
+ autoload :System, 'measurements/system'
13
+ autoload :Unit, 'measurements/unit'
14
+ autoload :Exception, 'measurements/exception'
15
+
16
+ # Load in the helper methods to be able to access them
17
+ require 'measurements/helpers'
18
+
19
+ end
@@ -0,0 +1,34 @@
1
+ abbreviations:
2
+ en:
3
+ singular:
4
+ pound: lb
5
+ cup: c
6
+ ounce: oz
7
+ tablespoon: tbsp
8
+ teaspoon: tsp
9
+ quart: qt
10
+ pint: pt
11
+ gallon: gal
12
+ inch: in
13
+ foot: ft
14
+ mile: mi
15
+ furlong: fur
16
+ thou: th
17
+ chain: ch
18
+ league: lea
19
+ plural:
20
+ pound: lbs
21
+ cup: c
22
+ ounce: oz
23
+ tablespoon: tbsp
24
+ teaspoon: tsp
25
+ quart: qt
26
+ pint: pt
27
+ gallon: gal
28
+ inch: in
29
+ foot: ft
30
+ mile: mi
31
+ furlong: fur
32
+ thou: th
33
+ chain: ch
34
+ league: lea
@@ -0,0 +1,31 @@
1
+ conversions:
2
+ cooking:
3
+ solid:
4
+ base: pound
5
+ cup: 2
6
+ ounce: 16
7
+ tablespoon: 32
8
+ teaspoon: 96
9
+ fluid:
10
+ base: gallon
11
+ quart: 4
12
+ pint: 8
13
+ cup: 16
14
+ ounce: 128
15
+ tablespoon: 256
16
+ teaspoon: 768
17
+ neutral:
18
+ base: cup
19
+ ounce: 8
20
+ tablespoon: 16
21
+ teaspoon: 48
22
+ imperial:
23
+ base:
24
+ base: league
25
+ mile: 3
26
+ furlong: 24
27
+ chain: 240
28
+ yard: 5280
29
+ foot: 15840
30
+ inch: 190080
31
+ thou: 190080000
@@ -0,0 +1,22 @@
1
+ module Measurements
2
+ module Exception
3
+
4
+ # This is an extention of the StandardError and it will be raised when
5
+ # you try to convert to a unit that doesn't exist. An example would
6
+ # be Ounce.new(3).convert_to :fake_unit
7
+ class NoUnitError < StandardError
8
+ end
9
+
10
+ # This is an extention of the StandardError and it will be raised when
11
+ # you try to convert a unit to a new unit that isn't in the same
12
+ # system type. Such as converting an ounce to a foot.
13
+ class InvalidConversionError < StandardError
14
+ end
15
+
16
+ # This is an extention of the StandardError and it will be raised when
17
+ # you try to set the type of a unit that doesn't allow it.
18
+ class InvalidTypeSettingError < StandardError
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ module Measurements
2
+
3
+ # Helper method to get what units are currently implemented
4
+ # @return [Array] an Array of currently implemented units
5
+ def available_units
6
+ Measurements::Unit.available_units
7
+ end
8
+
9
+ # Helper method to get what types are available
10
+ # @return [Array] an Array of types that are available
11
+ def available_types
12
+ Measurements::Type.available_types
13
+ end
14
+
15
+ # Helper method to create a unit class without having to explicitly
16
+ # call {Measurements::Unit::BaseUnit#initialize}
17
+ # @param [Symbol] klass the class of the unit you wish to initialize
18
+ # @param [Float] quantity the amount of the unit
19
+ # @param [Symbol] type optional unit type such as :solid or :fluid
20
+ # @return [BaseUnit] a new unit instance of class = klass
21
+ def new_unit(klass, quantity, type = nil)
22
+ if available_units.include? klass
23
+ unit = eval('Measurements::Unit::' + klass.to_s.capitalize).new quantity, type
24
+ Measurements::Unit::Unit.new(unit)
25
+ else
26
+ raise Measurements::Exception::NoUnitError, "The unit requested does not exist."
27
+ end
28
+ end
29
+
30
+ module_function :new_unit
31
+ module_function :available_units
32
+ module_function :available_types
33
+
34
+ class << self
35
+ alias_method :create_unit, :new_unit
36
+ end
37
+
38
+ end
@@ -0,0 +1,14 @@
1
+ module Measurements
2
+ module System
3
+
4
+ # System type for metric units
5
+ SI = "metric"
6
+
7
+ # System type for United States units
8
+ IMPERIAL = "imperial"
9
+
10
+ # System type for cooking units
11
+ COOK = "cooking"
12
+
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module Measurements
2
+ module Type
3
+
4
+ # Measurement state for unit of type solid
5
+ SOLID = "solid"
6
+
7
+ # Measurement state for unit of type fluid
8
+ FLUID = "fluid"
9
+
10
+ # Measurement state for unit that can be either a fluid or solid measure
11
+ NEUTRAL = "neutral"
12
+
13
+ # Measurement state for the unit that is not a cooking unit
14
+ BASE = "base"
15
+
16
+ # Helper method to get what types are available
17
+ # @deprecated Use {Measurements#available_types} instead of this method because
18
+ # it's a more obvious method to use instead of diving down to the Type module.
19
+ # @return [Array] an Array of types that are available
20
+ def available_types
21
+ self.constants.map{|types| types.downcase.to_sym}
22
+ end
23
+
24
+ module_function :available_types
25
+
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+
3
+ module Measurements
4
+ module Unit
5
+
6
+ # All conversions are loaded from the conversions.yml.
7
+ # The conversions.yml file lives in the /measurements directory of the project
8
+ CONVERSIONS = YAML::load(File.open(File.join(File.dirname(__FILE__), 'config', 'conversions.yml')))
9
+
10
+ # All abbreviations are loaded from the abbreviations.yml.
11
+ # The abbreviations.yml file lives in the /measurements/config directory of the project
12
+ ABBREVIATIONS = YAML::load(File.open(File.join(File.dirname(__FILE__), 'config', 'abbreviations.yml')))
13
+
14
+ autoload :BaseUnit, 'measurements/unit/baseunit'
15
+ autoload :Unit, 'measurements/unit/unit'
16
+
17
+ # Cooking Units
18
+ autoload :Ounce, 'measurements/unit/ounce'
19
+ autoload :Pound, 'measurements/unit/pound'
20
+ autoload :Teaspoon, 'measurements/unit/teaspoon'
21
+ autoload :Tablespoon, 'measurements/unit/tablespoon'
22
+ autoload :Cup, 'measurements/unit/cup'
23
+ autoload :Pint, 'measurements/unit/pint'
24
+ autoload :Quart, 'measurements/unit/quart'
25
+ autoload :Gallon, 'measurements/unit/gallon'
26
+
27
+ # Imperial Units
28
+ autoload :Inch, 'measurements/unit/inch'
29
+ autoload :Foot, 'measurements/unit/foot'
30
+ autoload :Yard, 'measurements/unit/yard'
31
+ autoload :Furlong, 'measurements/unit/furlong'
32
+ autoload :Chain, 'measurements/unit/chain'
33
+ autoload :Mile, 'measurements/unit/mile'
34
+ autoload :Thou, 'measurements/unit/thou'
35
+ autoload :League, 'measurements/unit/league'
36
+
37
+ # Helper method to get what units are currently implemented
38
+ # @deprecated Use {Measurements#available_units} instead of this method because
39
+ # it's a more obvious method to use instead of diving down to the Unit module.
40
+ # @return [Array] an Array of currently implemented units
41
+ def available_units
42
+ self.constants.reject{|unit| unit.eql?(:BaseUnit) ||
43
+ unit.eql?(:Unit) ||
44
+ unit.eql?(:CONVERSIONS) ||
45
+ unit.eql?(:ABBREVIATIONS)}.map{|unit| unit.to_s.downcase.to_sym}
46
+ end
47
+
48
+ module_function :available_units
49
+
50
+ end
51
+ end
@@ -0,0 +1,186 @@
1
+ module Measurements
2
+ module Unit
3
+ module BaseUnit
4
+
5
+ # @attribute [rw]
6
+ # The quantity of a unit
7
+ attr_accessor :quantity
8
+
9
+ def initialize(quantity, type = nil)
10
+ @quantity = quantity.to_f
11
+
12
+ if !type.nil?
13
+ self.type = type
14
+ else
15
+ @type = type
16
+ end
17
+ end
18
+
19
+ # Set the unit type of a unit manually. A type can only be set for units with the
20
+ # unit type of "neutral", any other unit type will raise and error.
21
+ # @attribute [w]
22
+ # @param [String] type the type of unit the unit should be.
23
+ def type=(type)
24
+ if self.unit_type.eql? "neutral"
25
+ @type = type
26
+ else
27
+ raise Measurements::Exception::InvalidTypeSettingError, "Types can only be set on neutral units."
28
+ end
29
+ end
30
+
31
+ # @attribute [r]
32
+ # The manual set unit_type
33
+ def type
34
+ @type
35
+ end
36
+
37
+ # @attribute [r]
38
+ # The type of the editable type of the unit. This is
39
+ # used to limit the conversions from fluid to solid
40
+ def unit
41
+ self.class.name.split('::').last.downcase.to_s
42
+ end
43
+
44
+ # @attribute [r]
45
+ # The system of measurement of the unit
46
+ def unit_system
47
+ eval(self.class.to_s + "::UNIT_SYSTEM")
48
+ end
49
+
50
+ # @attribute [r]
51
+ # The class type of the unit, such as Liquid or Neutral
52
+ def unit_type
53
+ eval(self.class.to_s + "::UNIT_TYPE")
54
+ end
55
+
56
+ # @attribute [r]
57
+ # The abbreviation of the unit depending on the locale
58
+ def unit_abbr
59
+ if @quantity <= 1
60
+ Measurements::Unit::ABBREVIATIONS["abbreviations"][Measurements::LOCALE]["singular"][self.unit]
61
+ else
62
+ Measurements::Unit::ABBREVIATIONS["abbreviations"][Measurements::LOCALE]["plural"][self.unit]
63
+ end
64
+ end
65
+
66
+ # When you look at a unit object the quantity will be displayed.
67
+ # It seemed like a nicer way to display the unit, kind of like a [Float]
68
+ def inspect
69
+ @quantity
70
+ end
71
+
72
+ # Convert the current unit into a new unit of the type given
73
+ # @param [Symbol] type the type of unit to convert to
74
+ # @return [BaseUnit] the new unit, it will be a subclass of [BaseUnit].
75
+ # @raise [InvalidConversionError] gets raised if the type of conversion is not valid.
76
+ def convert_to(type)
77
+ type = type.to_s
78
+
79
+ if !validate_system(type)
80
+ raise Measurements::Exception::InvalidConversionError, "A conversion must be from the same system type."
81
+ end
82
+
83
+ if validate_conversion(type)
84
+ base = convert_to_base(self, type)
85
+ return convert_to_type(base, type)
86
+ else
87
+ raise Measurements::Exception::InvalidConversionError, "A conversion must be from the same type or neutral."
88
+ end
89
+ end
90
+
91
+ # Convert the current unit into a new unit of highest bestfit unit that
92
+ # would make sense while measuring something. An example would be that a better way
93
+ # to display 6 tsps would be 2 tbsps.
94
+ # @return [BaseUnit] the new unit, it will be the unit that is the bestfit conversion.
95
+ def smart_convert
96
+ #TODO
97
+ end
98
+
99
+ private
100
+
101
+ # Get the unit type of the class passed in as a string
102
+ # @param [String] type the class name as a string
103
+ # @return [String] the unit type of the class that was passed in.
104
+ def unit_type_from_type(type)
105
+ eval("Measurements::Unit::" + type.capitalize + "::UNIT_TYPE")
106
+ end
107
+
108
+ # Get the unit system of the class passed in as a string
109
+ # @param [String] type the class name as a string
110
+ # @return [String] the unit system of the class that was passed in.
111
+ def unit_system_from_type(type)
112
+ eval("Measurements::Unit::" + type.capitalize + "::UNIT_SYSTEM")
113
+ end
114
+
115
+ # Validate if the systems of the converting units match. A conversion will only
116
+ # be allowed if the two units are from the same unit system.
117
+ # @param [String] to the unit type to convert to
118
+ # @return [Boolean] true if the conversion is valid.
119
+ # @raise [NoUnitError] gets raised if the from or to units could not be found
120
+ def validate_system(to)
121
+ from = self.unit
122
+
123
+ begin
124
+ from = unit_system_from_type from
125
+ to = unit_system_from_type to
126
+ rescue
127
+ raise Measurements::Exception::NoUnitError, "The unit you're trying to convert to does not exist."
128
+ end
129
+
130
+ from.eql? to
131
+ end
132
+
133
+ # Validate if a conversion will be valid. A conversion will be valid if one
134
+ # of the units types are neutral or if the unit types are the same.
135
+ # @param [String] to the unit type to convert to
136
+ # @return [Boolean] True if the conversion is valid.
137
+ # @raise [NoUnitError] gets raised if the from or to units could not be found
138
+ def validate_conversion(to)
139
+ from = self.unit
140
+
141
+ begin
142
+ from = unit_type_from_type from
143
+ to = unit_type_from_type to
144
+ rescue
145
+ raise Measurements::Exception::NoUnitError, "The unit you're trying to convert to does not exist."
146
+ end
147
+
148
+ (from.eql?("neutral") || to.eql?("neutral") || from.eql?(to)) && (self.type.nil? || self.type.eql?("neutral") || self.type.eql?(to))
149
+ end
150
+
151
+ # Convert the current unit into the base unit for its type.
152
+ # @param [BaseUnit] current the unit to be converted, it should be a subclass of [BaseUnit]
153
+ # @return [BaseUnit] the base unit.
154
+ def convert_to_base(current, type)
155
+ unit_type = unit_type_from_type type
156
+
157
+ if !unit_type.eql? "neutral"
158
+ measurement_list = Measurements::Unit::CONVERSIONS["conversions"][current.unit_system][unit_type]
159
+ else
160
+ measurement_list = Measurements::Unit::CONVERSIONS["conversions"][current.unit_system][current.unit_type]
161
+ end
162
+
163
+ quantity_to_mst = measurement_list[current.unit].to_f != 0.0 ? measurement_list[current.unit] : 1.0
164
+ base_type = measurement_list["base"]
165
+
166
+ eval("Measurements::Unit::" + base_type.capitalize).new(current.quantity / quantity_to_mst, current.type)
167
+ end
168
+
169
+ # Take the base unit and convert it to the requested type. If the type requested
170
+ # is the same type as the base, just return the base unit back.
171
+ # @param [BaseUnit] base the base unit to be converted
172
+ # @param [String] type the type of unit the base should be converted to
173
+ # @return [BaseUnit] the new unit, it will be a subclass of [BaseUnit]
174
+ def convert_to_type(base, type)
175
+ if base.unit.eql? type
176
+ return base
177
+ else
178
+ measurement_list = Measurements::Unit::CONVERSIONS["conversions"][base.unit_system][base.unit_type]
179
+ quantity_to_type = measurement_list[type]
180
+
181
+ eval("Measurements::Unit::" + type.capitalize).new(base.quantity * quantity_to_type, base.type)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a chain. A chain is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Chain
7
+ include BaseUnit
8
+
9
+ # Type for the Chain unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Chain unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a cup. A cup is a neutral unit that belongs to the cooking unit
5
+ # system.
6
+ class Cup
7
+ include BaseUnit
8
+
9
+ # Type for the Cup unit
10
+ UNIT_TYPE = Measurements::Type::NEUTRAL
11
+
12
+ # System type for the Cup unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a foot. A foot is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Foot
7
+ include BaseUnit
8
+
9
+ # Type for the Foot unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Foot unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a furlong. A furlong is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Furlong
7
+ include BaseUnit
8
+
9
+ # Type for the Furlong unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Furlong unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a gallon. A gallon is a fluid unit that belongs to the cooking unit
5
+ # system.
6
+ class Gallon
7
+ include BaseUnit
8
+
9
+ # Type for the Gallon unit
10
+ UNIT_TYPE = Measurements::Type::FLUID
11
+
12
+ # System type for the Gallon unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a inch. A inch is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Inch
7
+ include BaseUnit
8
+
9
+ # Type for the Inch unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Inch unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a league. A league is a unit that belongs to the imperial unit
5
+ # system.
6
+ class League
7
+ include BaseUnit
8
+
9
+ # Type for the League unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the League unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a mile. A mile is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Mile
7
+ include BaseUnit
8
+
9
+ # Type for the Mile unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Mile unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a ounce. A ounce is a neutral unit that belongs to the cooking unit
5
+ # system.
6
+ class Ounce
7
+ include BaseUnit
8
+
9
+ # Type for the Ounce unit
10
+ UNIT_TYPE = Measurements::Type::NEUTRAL
11
+
12
+ # System type for the Ounce unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a pint. A pint is a fluid unit that belongs to the cooking unit
5
+ # system.
6
+ class Pint
7
+ include BaseUnit
8
+
9
+ # Type for the Pint unit
10
+ UNIT_TYPE = Measurements::Type::FLUID
11
+
12
+ # System type for the Pint unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a pound. A pound is a solid unit that belongs to the cooking unit
5
+ # system.
6
+ class Pound
7
+ include BaseUnit
8
+
9
+ # Type for the Pound unit
10
+ UNIT_TYPE = Measurements::Type::SOLID
11
+
12
+ # System type for the Pound unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a quart. A quart is a fluid unit that belongs to the cooking unit
5
+ # system.
6
+ class Quart
7
+ include BaseUnit
8
+
9
+ # Type for the Quart unit
10
+ UNIT_TYPE = Measurements::Type::FLUID
11
+
12
+ # System type for the Quart unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a teaspoon. A teaspoon is a neutral unit that belongs to the cooking unit
5
+ # system.
6
+ class Tablespoon
7
+ include BaseUnit
8
+
9
+ # Type for the Tablespoon unit
10
+ UNIT_TYPE = Measurements::Type::NEUTRAL
11
+
12
+ # System type for the Tablespoon unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a teaspoon. A teaspoon is a neutral unit that belongs to the cooking unit
5
+ # system.
6
+ class Teaspoon
7
+ include BaseUnit
8
+
9
+ # Type for the Teaspoon unit
10
+ UNIT_TYPE = Measurements::Type::NEUTRAL
11
+
12
+ # System type for the Teaspoon unit
13
+ UNIT_SYSTEM = Measurements::System::COOK
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a thou. A thou is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Thou
7
+ include BaseUnit
8
+
9
+ # Type for the Thou unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Thou unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # A wrapper for all of the units, this class was added to make the conversion of classes
5
+ # keep the same object class instead of changing everytime you convert a unit.
6
+ class Unit
7
+
8
+ def initialize(klass)
9
+ @klass = klass
10
+ end
11
+
12
+ # @see Measurements::Unit::BaseUnit#inspect
13
+ def inspect
14
+ self.quantity
15
+ end
16
+
17
+ # A pretty print method that will return a human readable version
18
+ # of a unit
19
+ # @return [String] a human readable unit description
20
+ def humanize
21
+ return "#{self.quantity.to_s} #{self.unit_abbr}"
22
+ end
23
+
24
+ # @see Measurements::Unit::BaseUnit#type=
25
+ def type=(type)
26
+ @klass.type = type
27
+ end
28
+
29
+ # @see Measurements::Unit::BaseUnit#type
30
+ def type
31
+ @klass.type
32
+ end
33
+
34
+ # Get the quantity of the unit
35
+ # @return [Float] the quantitiy of the unit
36
+ def quantity
37
+ @klass.quantity
38
+ end
39
+
40
+ # Set the quantity of a unit
41
+ # @param [Float] quanity the quantity of the unit
42
+ def quantity=(quantity)
43
+ @klass.quantity = quantity
44
+ end
45
+
46
+ # @see Measurements::Unit::BaseUnit#unit
47
+ def unit
48
+ @klass.unit
49
+ end
50
+
51
+ # @see Measurements::Unit::BaseUnit#unit_type
52
+ def unit_type
53
+ @klass.unit_type
54
+ end
55
+
56
+ # @see Measurements::Unit::BaseUnit#unit_system
57
+ def unit_system
58
+ @klass.unit_system
59
+ end
60
+
61
+ # @see Measurements::Unit::BaseUnit#unit_abbr
62
+ def unit_abbr
63
+ @klass.unit_abbr
64
+ end
65
+
66
+ # This method will call the current units convert_to method. This is the dangerous version and it will
67
+ # change the callers unit.
68
+ # @see Measurements::Unit::BaseUnit#convert_to
69
+ def convert_to!(type)
70
+ @klass = @klass.convert_to(type)
71
+ end
72
+
73
+ # The safe version of converting a unit. This method will duplicate the caller object and return the new
74
+ # version of the object instead of the original caller
75
+ # @param [Symbol] type the unit to be converted to
76
+ # @return [Unit] the new Unit object
77
+ def convert_to(type)
78
+ new_unit = self.dup
79
+ new_unit.convert_to!(type)
80
+ new_unit
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,17 @@
1
+ module Measurements
2
+ module Unit
3
+
4
+ # The implementation of a yard. A yard is a unit that belongs to the imperial unit
5
+ # system.
6
+ class Yard
7
+ include BaseUnit
8
+
9
+ # Type for the Yard unit
10
+ UNIT_TYPE = Measurements::Type::BASE
11
+
12
+ # System type for the Yard unit
13
+ UNIT_SYSTEM = Measurements::System::IMPERIAL
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Measurements
2
+
3
+ # The current version of the gem, the numbers stand for
4
+ # major.minor.patch
5
+ Version = "0.3.0"
6
+
7
+ end
@@ -0,0 +1,18 @@
1
+ $:.unshift File.expand_path('../lib/', __FILE__)
2
+ require 'measurements/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'measurements'
6
+ spec.author = 'Casey Stehlik'
7
+ spec.email = 'casey.stehlik@stehlikc.net'
8
+ spec.homepage = 'http://www.stehlikc.net/'
9
+ spec.add_development_dependency 'yard'
10
+ spec.add_development_dependency 'redcarpet'
11
+ spec.add_development_dependency 'rspec'
12
+ spec.add_development_dependency 'guard-rspec'
13
+ spec.version = Measurements::Version
14
+ spec.require_paths = ['lib']
15
+ spec.files = `git ls-files`.split("\n")
16
+ spec.description = "A measurement conversion gem"
17
+ spec.summary = "A measurement conversion"
18
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__)+ '/lib')
2
+
3
+ require 'measurements'
4
+
@@ -0,0 +1,101 @@
1
+ require 'helper'
2
+
3
+ describe Measurements do
4
+ it "should return the available system types" do
5
+ Measurements.available_types.should == Measurements::Type.available_types
6
+ end
7
+
8
+ it "should return the available units that are implemented" do
9
+ Measurements.available_units.should == Measurements::Unit.available_units
10
+ end
11
+
12
+ it "should create a 3 ounces object" do
13
+ ounce = Measurements.create_unit :ounce, 3
14
+ ounce.unit.should == "ounce" && ounce.quantity.should == 3
15
+ end
16
+
17
+ it "should create a 4 pound object" do
18
+ pound = Measurements.new_unit :pound, 4
19
+ pound.unit.should == "pound" && pound.quantity.should == 4
20
+ end
21
+
22
+ it "should raise an error if the unit requested has not been implemented" do
23
+ expect{ Measurements.new_unit(:unimplemented_unit, 2) }.should raise_error()
24
+ end
25
+
26
+ it "should convert 1 quart to 4 cups" do
27
+ quart = Measurements.new_unit :quart, 1
28
+ cup = quart.convert_to :cup
29
+ cup.quantity.should == 4
30
+ end
31
+
32
+ it "should convert 8 cups to 0.5 gallons" do
33
+ cup = Measurements.new_unit :cup, 8
34
+ gallon = cup.convert_to :gallon
35
+ gallon.quantity.should == 0.5
36
+ end
37
+
38
+ it "should convert 6 teaspoons to 2 tablespoons" do
39
+ teaspoon = Measurements.new_unit :teaspoon, 6
40
+ tablespoon = teaspoon.convert_to :tablespoon
41
+ tablespoon.quantity.should == 2
42
+ end
43
+
44
+ it "should raise an error if the unit type is set to solid and you try to convert to a fluid" do
45
+ cup = Measurements.new_unit :cup, 2, :solid
46
+ expect{ ounce = cup.convert_to(:gallon) }.should raise_error()
47
+ end
48
+
49
+ it "should raise an error if the unit type is set to fluid and you try to convert to a solid" do
50
+ cup = Measurements.new_unit :gallon, 1
51
+ expect{ ounce = cup.convert_to(:pound) }.should raise_error()
52
+ end
53
+
54
+ it "should raise an error if you try to set the unit type of a fluid only unit" do
55
+ expect{ gallon = Measurements.new_unit :gallon, 1, :fluid }.should raise_error()
56
+ end
57
+
58
+ it "should raise an error if you try to set the unit type of a solid only unit" do
59
+ expect{ pound = Measurements.new_unit :pound, 2, :solid }.should raise_error()
60
+ end
61
+
62
+ it "should raise an error when trying to set the unit type of a unit on the fly" do
63
+ cup = Measurements.new_unit :gallon, 2
64
+ expect { cup.type = :solid }.should raise_error()
65
+ end
66
+
67
+ it "should convert 2 feet to 24 inches" do
68
+ feet = Measurements.new_unit :foot, 2
69
+ inches = feet.convert_to :inch
70
+ inches.quantity.should == 24
71
+ end
72
+
73
+ it "should convert 30 inches to 1.5 feet" do
74
+ inches = Measurements.new_unit :inch, 30
75
+ feet = inches.convert_to :foot
76
+ feet.quantity.should == 2.5
77
+ end
78
+
79
+ it "should raise an error if you try to convert a unit across system types" do
80
+ inches = Measurements.new_unit :inch, 55
81
+ expect { cup = inches.convert_to :cup }.should raise_error()
82
+ end
83
+
84
+ it "should raise an error if you try to set a type manually for imperial units" do
85
+ expect { inch = Measurements.new_unit :inch, 51, :solid }.should raise_error()
86
+ end
87
+
88
+ it "should convert 2.5 leagues to 475200 inches" do
89
+ leagues = Measurements.new_unit :league, 2.5
90
+ inches = leagues.convert_to :inch
91
+ inches.quantity.should == 475200
92
+ end
93
+
94
+ it "should humanize 3.5 ounces to 3.5 oz" do
95
+ Measurements.new_unit(:ounce, 3.5).humanize.should == "3.5 oz"
96
+ end
97
+
98
+ it "should humanize 4 pounds to 4 lbs" do
99
+ Measurements.new_unit(:pound, 4).humanize.should == "4.0 lbs"
100
+ end
101
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: measurements
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Casey Stehlik
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: yard
16
+ requirement: &70325023315160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70325023315160
25
+ - !ruby/object:Gem::Dependency
26
+ name: redcarpet
27
+ requirement: &70325023314240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70325023314240
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70325023313740 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70325023313740
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-rspec
49
+ requirement: &70325023312860 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70325023312860
58
+ description: A measurement conversion gem
59
+ email: casey.stehlik@stehlikc.net
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - .gitignore
65
+ - .rspec
66
+ - .yardopts
67
+ - Gemfile
68
+ - Guardfile
69
+ - README.md
70
+ - Rakefile
71
+ - lib/measurements.rb
72
+ - lib/measurements/config/abbreviations.yml
73
+ - lib/measurements/config/conversions.yml
74
+ - lib/measurements/exception.rb
75
+ - lib/measurements/helpers.rb
76
+ - lib/measurements/system.rb
77
+ - lib/measurements/type.rb
78
+ - lib/measurements/unit.rb
79
+ - lib/measurements/unit/baseunit.rb
80
+ - lib/measurements/unit/chain.rb
81
+ - lib/measurements/unit/cup.rb
82
+ - lib/measurements/unit/foot.rb
83
+ - lib/measurements/unit/furlong.rb
84
+ - lib/measurements/unit/gallon.rb
85
+ - lib/measurements/unit/inch.rb
86
+ - lib/measurements/unit/league.rb
87
+ - lib/measurements/unit/mile.rb
88
+ - lib/measurements/unit/ounce.rb
89
+ - lib/measurements/unit/pint.rb
90
+ - lib/measurements/unit/pound.rb
91
+ - lib/measurements/unit/quart.rb
92
+ - lib/measurements/unit/tablespoon.rb
93
+ - lib/measurements/unit/teaspoon.rb
94
+ - lib/measurements/unit/thou.rb
95
+ - lib/measurements/unit/unit.rb
96
+ - lib/measurements/unit/yard.rb
97
+ - lib/measurements/version.rb
98
+ - measurements.gemspec
99
+ - spec/helper.rb
100
+ - spec/measurements_spec.rb
101
+ homepage: http://www.stehlikc.net/
102
+ licenses: []
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 1.8.15
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: A measurement conversion
125
+ test_files: []
126
+ has_rdoc: