basic_temperature 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +33 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +12 -0
- data/.travis.yml +2 -1
- data/README.md +223 -9
- data/basic_temperature.gemspec +6 -1
- data/bin/console +4 -4
- data/docs/apple-touch-icon.png +0 -0
- data/docs/classes/BasicTemperature.html +1227 -0
- data/docs/classes/BasicTemperature/InitializationArgumentsError.html +94 -0
- data/docs/classes/BasicTemperature/InvalidDegreesError.html +94 -0
- data/docs/classes/BasicTemperature/InvalidNumericOrTemperatureError.html +94 -0
- data/docs/classes/BasicTemperature/InvalidScaleError.html +94 -0
- data/docs/classes/Object.html +274 -0
- data/docs/classes/Temperature.html +1227 -0
- data/docs/created.rid +4 -0
- data/docs/css/github.css +123 -0
- data/docs/css/main.css +374 -0
- data/docs/css/panel.css +361 -0
- data/docs/css/reset.css +48 -0
- data/docs/favicon.ico +0 -0
- data/docs/files/lib/basic_temperature/temperature_rb.html +73 -0
- data/docs/files/lib/basic_temperature/version_rb.html +84 -0
- data/docs/files/lib/basic_temperature_rb.html +112 -0
- data/docs/i/arrow-down.svg +8 -0
- data/docs/i/arrow-right.svg +8 -0
- data/docs/i/search.svg +12 -0
- data/docs/i/tree_bg.svg +8 -0
- data/docs/index.html +11 -0
- data/docs/js/highlight.pack.js +1 -0
- data/docs/js/jquery-1.3.2.min.js +19 -0
- data/docs/js/main.js +25 -0
- data/docs/js/navigation.js +105 -0
- data/docs/js/navigation.js.gz +0 -0
- data/docs/js/search_index.js +1 -0
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searchdoc.js +465 -0
- data/docs/js/searcher.js +229 -0
- data/docs/js/searcher.js.gz +0 -0
- data/docs/panel/index.html +47 -0
- data/docs/panel/links.html +12 -0
- data/docs/panel/tree.js +1 -0
- data/lib/basic_temperature.rb +552 -75
- data/lib/basic_temperature/temperature.rb +5 -0
- data/lib/basic_temperature/version.rb +1 -1
- data/logo.png +0 -0
- metadata +112 -4
- data/Gemfile.lock +0 -54
data/docs/js/searcher.js
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
Searcher = function(data) {
|
2
|
+
this.data = data;
|
3
|
+
this.handlers = [];
|
4
|
+
}
|
5
|
+
|
6
|
+
Searcher.prototype = new function() {
|
7
|
+
// search is performed in chunks of 1000 for non-blocking user input
|
8
|
+
var CHUNK_SIZE = 1000;
|
9
|
+
// do not try to find more than 100 results
|
10
|
+
var MAX_RESULTS = 100;
|
11
|
+
var huid = 1;
|
12
|
+
var suid = 1;
|
13
|
+
var runs = 0;
|
14
|
+
|
15
|
+
this.find = function(query) {
|
16
|
+
var queries = splitQuery(query);
|
17
|
+
var regexps = buildRegexps(queries);
|
18
|
+
var highlighters = buildHilighters(queries);
|
19
|
+
var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
|
20
|
+
var _this = this;
|
21
|
+
|
22
|
+
this.currentSuid = state.n;
|
23
|
+
|
24
|
+
if (!query) return;
|
25
|
+
|
26
|
+
var run = function() {
|
27
|
+
// stop current search thread if new search started
|
28
|
+
if (state.n != _this.currentSuid) return;
|
29
|
+
|
30
|
+
var results =
|
31
|
+
performSearch(_this.data, regexps, queries, highlighters, state);
|
32
|
+
var hasMore = (state.limit > 0 && state.pass < 4);
|
33
|
+
|
34
|
+
triggerResults.call(_this, results, !hasMore);
|
35
|
+
if (hasMore) {
|
36
|
+
setTimeout(run, 2);
|
37
|
+
}
|
38
|
+
runs++;
|
39
|
+
};
|
40
|
+
runs = 0;
|
41
|
+
|
42
|
+
// start search thread
|
43
|
+
run();
|
44
|
+
}
|
45
|
+
|
46
|
+
/* ----- Events ------ */
|
47
|
+
this.ready = function(fn) {
|
48
|
+
fn.huid = huid;
|
49
|
+
this.handlers.push(fn);
|
50
|
+
}
|
51
|
+
|
52
|
+
/* ----- Utilities ------ */
|
53
|
+
function splitQuery(query) {
|
54
|
+
return query.split(/(\s+|::?|\(\)?)/).filter(function(string) {
|
55
|
+
return string.match(/\S/);
|
56
|
+
});
|
57
|
+
}
|
58
|
+
|
59
|
+
function buildRegexps(queries) {
|
60
|
+
return queries.map(function(query) {
|
61
|
+
return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i');
|
62
|
+
});
|
63
|
+
}
|
64
|
+
|
65
|
+
function buildHilighters(queries) {
|
66
|
+
return queries.map(function(query) {
|
67
|
+
return query.split('').map(function(l, i) {
|
68
|
+
return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
|
69
|
+
}).join('');
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
// function longMatchRegexp(index, longIndex, regexps) {
|
74
|
+
// for (var i = regexps.length - 1; i >= 0; i--){
|
75
|
+
// if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
|
76
|
+
// };
|
77
|
+
// return true;
|
78
|
+
// }
|
79
|
+
|
80
|
+
|
81
|
+
/* ----- Mathchers ------ */
|
82
|
+
|
83
|
+
/*
|
84
|
+
* This record matches if the index starts with queries[0] and the record
|
85
|
+
* matches all of the regexps
|
86
|
+
*/
|
87
|
+
function matchPassBeginning(index, longIndex, queries, regexps) {
|
88
|
+
if (index.indexOf(queries[0]) != 0) return false;
|
89
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
90
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
91
|
+
return false;
|
92
|
+
};
|
93
|
+
return true;
|
94
|
+
}
|
95
|
+
|
96
|
+
/*
|
97
|
+
* This record matches if the longIndex starts with queries[0] and the
|
98
|
+
* longIndex matches all of the regexps
|
99
|
+
*/
|
100
|
+
function matchPassLongIndex(index, longIndex, queries, regexps) {
|
101
|
+
if (longIndex.indexOf(queries[0]) != 0) return false;
|
102
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
103
|
+
if (!longIndex.match(regexps[i]))
|
104
|
+
return false;
|
105
|
+
};
|
106
|
+
return true;
|
107
|
+
}
|
108
|
+
|
109
|
+
/*
|
110
|
+
* This record matches if the index contains queries[0] and the record
|
111
|
+
* matches all of the regexps
|
112
|
+
*/
|
113
|
+
function matchPassContains(index, longIndex, queries, regexps) {
|
114
|
+
if (index.indexOf(queries[0]) == -1) return false;
|
115
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
116
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
117
|
+
return false;
|
118
|
+
};
|
119
|
+
return true;
|
120
|
+
}
|
121
|
+
|
122
|
+
/*
|
123
|
+
* This record matches if regexps[0] matches the index and the record
|
124
|
+
* matches all of the regexps
|
125
|
+
*/
|
126
|
+
function matchPassRegexp(index, longIndex, queries, regexps) {
|
127
|
+
if (!index.match(regexps[0])) return false;
|
128
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
129
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
130
|
+
return false;
|
131
|
+
};
|
132
|
+
return true;
|
133
|
+
}
|
134
|
+
|
135
|
+
|
136
|
+
/* ----- Highlighters ------ */
|
137
|
+
function highlightRegexp(info, queries, regexps, highlighters) {
|
138
|
+
var result = createResult(info);
|
139
|
+
for (var i=0, l = regexps.length; i < l; i++) {
|
140
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
141
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
142
|
+
};
|
143
|
+
return result;
|
144
|
+
}
|
145
|
+
|
146
|
+
function hltSubstring(string, pos, length) {
|
147
|
+
return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
|
148
|
+
}
|
149
|
+
|
150
|
+
function highlightQuery(info, queries, regexps, highlighters) {
|
151
|
+
var result = createResult(info);
|
152
|
+
var pos = 0;
|
153
|
+
var lcTitle = result.title.toLowerCase();
|
154
|
+
|
155
|
+
pos = lcTitle.indexOf(queries[0]);
|
156
|
+
if (pos != -1) {
|
157
|
+
result.title = hltSubstring(result.title, pos, queries[0].length);
|
158
|
+
}
|
159
|
+
|
160
|
+
result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
|
161
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
162
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
163
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
164
|
+
};
|
165
|
+
return result;
|
166
|
+
}
|
167
|
+
|
168
|
+
function createResult(info) {
|
169
|
+
var result = {};
|
170
|
+
result.title = info[0];
|
171
|
+
result.namespace = info[1];
|
172
|
+
result.path = info[2];
|
173
|
+
result.params = info[3];
|
174
|
+
result.snippet = info[4];
|
175
|
+
result.badge = info[6];
|
176
|
+
return result;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* ----- Searching ------ */
|
180
|
+
function performSearch(data, regexps, queries, highlighters, state) {
|
181
|
+
var searchIndex = data.searchIndex;
|
182
|
+
var longSearchIndex = data.longSearchIndex;
|
183
|
+
var info = data.info;
|
184
|
+
var result = [];
|
185
|
+
var i = state.from;
|
186
|
+
var l = searchIndex.length;
|
187
|
+
var togo = CHUNK_SIZE;
|
188
|
+
var matchFunc, hltFunc;
|
189
|
+
|
190
|
+
while (state.pass < 4 && state.limit > 0 && togo > 0) {
|
191
|
+
if (state.pass == 0) {
|
192
|
+
matchFunc = matchPassBeginning;
|
193
|
+
hltFunc = highlightQuery;
|
194
|
+
} else if (state.pass == 1) {
|
195
|
+
matchFunc = matchPassLongIndex;
|
196
|
+
hltFunc = highlightQuery;
|
197
|
+
} else if (state.pass == 2) {
|
198
|
+
matchFunc = matchPassContains;
|
199
|
+
hltFunc = highlightQuery;
|
200
|
+
} else if (state.pass == 3) {
|
201
|
+
matchFunc = matchPassRegexp;
|
202
|
+
hltFunc = highlightRegexp;
|
203
|
+
}
|
204
|
+
|
205
|
+
for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
|
206
|
+
if (info[i].n == state.n) continue;
|
207
|
+
if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
|
208
|
+
info[i].n = state.n;
|
209
|
+
result.push(hltFunc(info[i], queries, regexps, highlighters));
|
210
|
+
state.limit--;
|
211
|
+
}
|
212
|
+
};
|
213
|
+
if (searchIndex.length <= i) {
|
214
|
+
state.pass++;
|
215
|
+
i = state.from = 0;
|
216
|
+
} else {
|
217
|
+
state.from = i;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
return result;
|
221
|
+
}
|
222
|
+
|
223
|
+
function triggerResults(results, isLast) {
|
224
|
+
this.handlers.forEach(function(fn) {
|
225
|
+
fn.call(this, results, isLast)
|
226
|
+
});
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
Binary file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<title>search index</title>
|
5
|
+
<link rel="stylesheet" href="../css/reset.css" type="text/css" media="screen" charset="utf-8" />
|
6
|
+
<link rel="stylesheet" href="../css/panel.css" type="text/css" media="screen" charset="utf-8" />
|
7
|
+
<script src="../js/search_index.js" type="text/javascript" charset="utf-8"></script>
|
8
|
+
<script src="../js/searcher.js" type="text/javascript" charset="utf-8"></script>
|
9
|
+
<script src="tree.js" type="text/javascript" charset="utf-8"></script>
|
10
|
+
<script src="../js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
|
11
|
+
<script src="../js/searchdoc.js" type="text/javascript" charset="utf-8"></script>
|
12
|
+
<script type="text/javascript" charset="utf-8">
|
13
|
+
$(function() {
|
14
|
+
$('#links').hide();
|
15
|
+
var panel = new Searchdoc.Panel($('#panel'), search_data, tree, top.frames[1]);
|
16
|
+
$('#search').focus();
|
17
|
+
|
18
|
+
var s = window.parent.location.search.match(/\?q=([^&]+)/);
|
19
|
+
if (s) {
|
20
|
+
s = decodeURIComponent(s[1]).replace(/\+/g, ' ');
|
21
|
+
if (s.length > 0) {
|
22
|
+
$('#search').val(s);
|
23
|
+
panel.search(s, true);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
})
|
27
|
+
</script>
|
28
|
+
</head>
|
29
|
+
|
30
|
+
<body>
|
31
|
+
<div class="panel panel_tree" id="panel">
|
32
|
+
<div class="header">
|
33
|
+
<input type="text" placeholder="Search for a class, method, ..." autosave="searchdoc" results="10" id="search" autocomplete="off" />
|
34
|
+
</div>
|
35
|
+
<div class="tree">
|
36
|
+
<ul>
|
37
|
+
</ul>
|
38
|
+
</div>
|
39
|
+
<div class="result">
|
40
|
+
<ul>
|
41
|
+
</ul>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
<a href="links.html" id="links">index</a>
|
45
|
+
</body>
|
46
|
+
|
47
|
+
</html>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<html>
|
2
|
+
<head>File index</head>
|
3
|
+
<body>
|
4
|
+
|
5
|
+
<a href="../files/lib/basic_temperature_rb.html">lib/basic_temperature.rb</a>
|
6
|
+
|
7
|
+
<a href="../files/lib/basic_temperature/temperature_rb.html">lib/basic_temperature/temperature.rb</a>
|
8
|
+
|
9
|
+
<a href="../files/lib/basic_temperature/version_rb.html">lib/basic_temperature/version.rb</a>
|
10
|
+
|
11
|
+
</body>
|
12
|
+
</html>
|
data/docs/panel/tree.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
var tree = [["","","files",[["","","lib",[["","","basic_temperature",[["temperature.rb","files/lib/basic_temperature/temperature_rb.html","",[]],["version.rb","files/lib/basic_temperature/version_rb.html","",[]]]],["basic_temperature.rb","files/lib/basic_temperature_rb.html","",[]]]]]],["Temperature","classes/BasicTemperature.html"," < Object",[]],["Object","classes/Object.html"," < BasicObject",[]],["Temperature","classes/Temperature.html"," < Object",[]]]
|
data/lib/basic_temperature.rb
CHANGED
@@ -2,28 +2,229 @@
|
|
2
2
|
|
3
3
|
require 'basic_temperature/version'
|
4
4
|
|
5
|
-
# Value Object for basic temperature operations like conversions from Celcius to Fahrenhait or Kelvin etc.
|
6
5
|
# rubocop:disable Metrics/ClassLength
|
6
|
+
|
7
|
+
##
|
8
|
+
# Temperature is a simple {Value Object}[https://martinfowler.com/bliki/ValueObject.html] for basic
|
9
|
+
# temperature operations like conversions from <tt>Celsius</tt> to <tt>Fahrenhait</tt> or <tt>Kelvin</tt>
|
10
|
+
# etc.
|
11
|
+
#
|
12
|
+
# Supported scales: <tt>Celsius</tt>, <tt>Fahrenheit</tt>, <tt>Kelvin</tt> and <tt>Rankine</tt>.
|
13
|
+
#
|
14
|
+
# == Creating Temperatures
|
15
|
+
#
|
16
|
+
# A new temperature can be created in multiple ways:
|
17
|
+
#
|
18
|
+
# - Using keyword arguments:
|
19
|
+
#
|
20
|
+
# Temperature.new(degrees: 0, scale: :celsius)
|
21
|
+
#
|
22
|
+
# - Using positional arguments:
|
23
|
+
#
|
24
|
+
# Temperature.new(0, :celsius)
|
25
|
+
#
|
26
|
+
# - Even more concise way using <tt>Temperature.[]</tt> (an alias of <tt>Temperature.new</tt>):
|
27
|
+
#
|
28
|
+
# Temperature[0, :celsius]
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# == Creating Temperatures from already existing temperature objects
|
32
|
+
#
|
33
|
+
# Sometimes it is useful to create a new temperature from already existing one.
|
34
|
+
#
|
35
|
+
# For such cases, there are {set_degrees}[rdoc-ref:BasicTemperature#set_degrees and
|
36
|
+
# {set_scale}[rdoc-ref:BasicTemperature#set_scale].
|
37
|
+
#
|
38
|
+
# Since temperatures are {Value Objects}[https://martinfowler.com/bliki/ValueObject.html], both methods
|
39
|
+
# returns new instances.
|
40
|
+
#
|
41
|
+
# Examples:
|
42
|
+
#
|
43
|
+
# temperature = Temperature[0, :celsius]
|
44
|
+
# # => 0 °C
|
45
|
+
#
|
46
|
+
# new_temperature = temperature.set_degrees(15)
|
47
|
+
# # => 15 °C
|
48
|
+
#
|
49
|
+
# temperature = Temperature[0, :celsius]
|
50
|
+
# # => 0 °C
|
51
|
+
#
|
52
|
+
# new_temperature = temperature.set_scale(:kelvin)
|
53
|
+
# # => 0 K
|
54
|
+
#
|
55
|
+
# == Conversions
|
56
|
+
#
|
57
|
+
# Temperatures can be converted to diffirent scales.
|
58
|
+
#
|
59
|
+
# Currently, the following scales are supported: <tt>Celsius</tt>, <tt>Fahrenheit</tt>, <tt>Kelvin</tt> and
|
60
|
+
# <tt>Rankine</tt>.
|
61
|
+
#
|
62
|
+
# Temperature[20, :celsius].to_celsius
|
63
|
+
# # => 20 °C
|
64
|
+
#
|
65
|
+
# Temperature[20, :celsius].to_fahrenheit
|
66
|
+
# # => 68 °F
|
67
|
+
#
|
68
|
+
# Temperature[20, :celsius].to_kelvin
|
69
|
+
# # => 293.15 K
|
70
|
+
#
|
71
|
+
# Temperature[20, :celsius].to_rankine
|
72
|
+
# # => 527.67 °R
|
73
|
+
#
|
74
|
+
# If it is necessary to convert scale dynamically, {to_scale}[rdoc-ref:BasicTemperature#to_scale] method is
|
75
|
+
# available.
|
76
|
+
#
|
77
|
+
# Temperature[20, :celsius].to_scale(scale)
|
78
|
+
#
|
79
|
+
# All conversion formulas are taken from
|
80
|
+
# {RapidTables}[https://www.rapidtables.com/convert/temperature/index.html].
|
81
|
+
#
|
82
|
+
# Conversion precision: 2 accurate digits after the decimal dot.
|
83
|
+
#
|
84
|
+
# == Comparison
|
85
|
+
#
|
86
|
+
# Temperature implements idiomatic {<=> spaceship operator}[https://ruby-doc.org/core/Comparable.html] and
|
87
|
+
# mixes in {Comparable}[https://ruby-doc.org/core/Comparable.html] module.
|
88
|
+
#
|
89
|
+
# As a result, all methods from Comparable are available, e.g:
|
90
|
+
#
|
91
|
+
# Temperature[20, :celsius] < Temperature[25, :celsius]
|
92
|
+
# # => true
|
93
|
+
#
|
94
|
+
# Temperature[20, :celsius] <= Temperature[25, :celsius]
|
95
|
+
# # => true
|
96
|
+
#
|
97
|
+
# Temperature[20, :celsius] == Temperature[25, :celsius]
|
98
|
+
# # => false
|
99
|
+
#
|
100
|
+
# Temperature[20, :celsius] > Temperature[25, :celsius]
|
101
|
+
# # => false
|
102
|
+
#
|
103
|
+
# Temperature[20, :celsius] >= Temperature[25, :celsius]
|
104
|
+
# # => false
|
105
|
+
#
|
106
|
+
# Temperature[20, :celsius].between?(Temperature[15, :celsius], Temperature[25, :celsius])
|
107
|
+
# # => true
|
108
|
+
#
|
109
|
+
# # Starting from Ruby 2.4.6
|
110
|
+
# Temperature[20, :celsius].clamp(Temperature[20, :celsius], Temperature[25, :celsius])
|
111
|
+
# # => 20 °C
|
112
|
+
#
|
113
|
+
# Please note, if <tt>other</tt> temperature has a different scale, temperature is automatically converted
|
114
|
+
# to that scale before comparison.
|
115
|
+
#
|
116
|
+
# Temperature[20, :celsius] == Temperature[293.15, :kelvin]
|
117
|
+
# # => true
|
118
|
+
#
|
119
|
+
# IMPORTANT !!!
|
120
|
+
#
|
121
|
+
# <tt>degrees</tt> are rounded to the nearest value with a precision of 2 decimal digits before comparison.
|
122
|
+
#
|
123
|
+
# This means the following temperatures are considered as equal:
|
124
|
+
#
|
125
|
+
# Temperature[20.020, :celsius] == Temperature[20.024, :celsius]
|
126
|
+
# # => true
|
127
|
+
#
|
128
|
+
# Temperature[20.025, :celsius] == Temperature[20.029, :celsius]
|
129
|
+
# # => true
|
130
|
+
#
|
131
|
+
# while these ones are treated as NOT equal:
|
132
|
+
#
|
133
|
+
# Temperature[20.024, :celsius] == Temperature[20.029, :celsius]
|
134
|
+
# # => false
|
135
|
+
#
|
136
|
+
# == Math
|
137
|
+
#
|
138
|
+
# ==== Addition/Subtraction.
|
139
|
+
#
|
140
|
+
# Temperature[20, :celsius] + Temperature[10, :celsius]
|
141
|
+
# # => 30 °C
|
142
|
+
#
|
143
|
+
# Temperature[20, :celsius] - Temperature[10, :celsius]
|
144
|
+
# # => 10 °C
|
145
|
+
#
|
146
|
+
# If second temperature has a different scale, first temperature is automatically converted to that scale
|
147
|
+
# before <tt>degrees</tt> addition/subtraction.
|
148
|
+
#
|
149
|
+
# Temperature[283.15, :kelvin] + Temperature[10, :celsius]
|
150
|
+
# # => 10 °C
|
151
|
+
#
|
152
|
+
# Returned temperature will have the same scale as the second temperature.
|
153
|
+
#
|
154
|
+
# It is possible to add/subtract numerics.
|
155
|
+
#
|
156
|
+
# Temperature[20, :celsius] + 10
|
157
|
+
# # => 30 °C
|
158
|
+
#
|
159
|
+
# Temperature[20, :celsius] - 10
|
160
|
+
# # => 10 °C
|
161
|
+
#
|
162
|
+
# In such cases, returned temperature will have the same scale as the first temperature.
|
163
|
+
#
|
164
|
+
# Also {Ruby coersion mechanism}[https://ruby-doc.org/core/Numeric.html#method-i-coerce] is supported.
|
165
|
+
#
|
166
|
+
# 10 + Temperature[20, :celsius]
|
167
|
+
# # => 30 °C
|
168
|
+
#
|
169
|
+
# 10 - Temperature[20, :celsius]
|
170
|
+
# # => -10 °C
|
171
|
+
#
|
172
|
+
# ==== Negation
|
173
|
+
#
|
174
|
+
# -Temperature[20, :celsius]
|
175
|
+
# # => -20 °C
|
176
|
+
#
|
7
177
|
class BasicTemperature
|
178
|
+
include Comparable
|
179
|
+
|
180
|
+
# Raised when <tt>Temperature.new</tt> is called with mixed positional and keyword arguments or without
|
181
|
+
# arguments at all.
|
8
182
|
class InitializationArgumentsError < StandardError; end
|
183
|
+
|
184
|
+
# Raised when <tt>degrees</tt> is not a Numeric.
|
9
185
|
class InvalidDegreesError < StandardError; end
|
186
|
+
|
187
|
+
# Raised when <tt>scale</tt> can not be casted to any possible scale value.
|
188
|
+
# See {SCALES}[rdoc-ref:BasicTemperature::SCALES].
|
10
189
|
class InvalidScaleError < StandardError; end
|
11
|
-
class InvalidOtherError < StandardError; end
|
12
|
-
class CoersionError < StandardError; end
|
13
190
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
191
|
+
# Raised when <tt>other</tt> is not a Numeric or Temperature in math operations.
|
192
|
+
class InvalidNumericOrTemperatureError < StandardError; end
|
193
|
+
|
194
|
+
CELSIUS = 'celsius'
|
195
|
+
FAHRENHEIT = 'fahrenheit'
|
196
|
+
KELVIN = 'kelvin'
|
197
|
+
RANKINE = 'rankine'
|
21
198
|
|
22
|
-
|
199
|
+
# A list of all currently supported scale values.
|
200
|
+
SCALES = [CELSIUS, FAHRENHEIT, KELVIN, RANKINE].freeze
|
201
|
+
|
202
|
+
# Degrees of the temperature.
|
203
|
+
attr_reader :degrees
|
204
|
+
|
205
|
+
# Scale of the temperature. Look at {SCALES}[rdoc-ref:BasicTemperature::SCALES] for possible values.
|
206
|
+
attr_reader :scale
|
207
|
+
|
208
|
+
##
|
209
|
+
# Creates a new instance of Temperature. Alias for <tt>new</tt>.
|
210
|
+
#
|
211
|
+
# :call-seq:
|
212
|
+
# [](degrees:, scale:)
|
213
|
+
# [](degrees, scale)
|
214
|
+
#
|
215
|
+
def self.[](*args, **kwargs)
|
216
|
+
new(*args, **kwargs)
|
217
|
+
end
|
23
218
|
|
219
|
+
##
|
220
|
+
# Creates a new instance of Temperature. Is aliased as <tt>[]</tt>.
|
221
|
+
#
|
222
|
+
# :call-seq:
|
223
|
+
# new(degrees:, scale:)
|
224
|
+
# new(degrees, scale)
|
225
|
+
#
|
24
226
|
def initialize(*positional_arguments, **keyword_arguments)
|
25
|
-
|
26
|
-
raise_initialization_arguments_error if positional_arguments.none? && keyword_arguments.none?
|
227
|
+
assert_either_positional_arguments_or_keyword_arguments!(positional_arguments, keyword_arguments)
|
27
228
|
|
28
229
|
if keyword_arguments.any?
|
29
230
|
initialize_via_keywords_arguments(keyword_arguments)
|
@@ -33,124 +234,352 @@ class BasicTemperature
|
|
33
234
|
end
|
34
235
|
|
35
236
|
# rubocop:disable Naming/AccessorMethodName
|
237
|
+
|
238
|
+
# Returns a new Temperature with updated <tt>degrees</tt>.
|
239
|
+
#
|
240
|
+
# temperature = Temperature[0, :celsius]
|
241
|
+
# # => 0 °C
|
242
|
+
#
|
243
|
+
# new_temperature = temperature.set_degrees(15)
|
244
|
+
# # => 15 °C
|
245
|
+
#
|
36
246
|
def set_degrees(degrees)
|
37
247
|
BasicTemperature.new(degrees, scale)
|
38
248
|
end
|
39
249
|
# rubocop:enable Naming/AccessorMethodName
|
40
250
|
|
41
251
|
# rubocop:disable Naming/AccessorMethodName
|
252
|
+
|
253
|
+
# Returns a new Temperature with updated <tt>scale</tt>.
|
254
|
+
#
|
255
|
+
# temperature = Temperature[0, :celsius]
|
256
|
+
# # => 0 °C
|
257
|
+
#
|
258
|
+
# new_temperature = temperature.set_scale(:kelvin)
|
259
|
+
# # => 0 K
|
260
|
+
#
|
42
261
|
def set_scale(scale)
|
43
|
-
|
262
|
+
BasicTemperature.new(degrees, scale)
|
44
263
|
end
|
45
264
|
# rubocop:enable Naming/AccessorMethodName
|
46
265
|
|
266
|
+
##
|
267
|
+
# Converts temperature to specific <tt>scale</tt>.
|
268
|
+
# If temperature is already in desired <tt>scale</tt>, returns current temperature object.
|
269
|
+
#
|
270
|
+
# Raises {InvalidScaleError}[rdoc-ref:BasicTemperature::InvalidScaleError]
|
271
|
+
# when <tt>scale</tt> can not be casted to any possible scale value
|
272
|
+
# (see {SCALES}[rdoc-ref:BasicTemperature::SCALES]).
|
273
|
+
#
|
274
|
+
# Temperature[60, :fahrenheit].to_scale(:celsius)
|
275
|
+
# # => 15.56 °C
|
276
|
+
#
|
47
277
|
def to_scale(scale)
|
48
|
-
|
49
|
-
|
278
|
+
casted_scale = cast_scale(scale)
|
279
|
+
|
280
|
+
assert_valid_scale!(casted_scale)
|
281
|
+
|
282
|
+
case casted_scale
|
283
|
+
when CELSIUS
|
50
284
|
to_celsius
|
51
285
|
when FAHRENHEIT
|
52
286
|
to_fahrenheit
|
53
287
|
when KELVIN
|
54
288
|
to_kelvin
|
55
|
-
|
56
|
-
|
289
|
+
when RANKINE
|
290
|
+
to_rankine
|
57
291
|
end
|
58
292
|
end
|
59
293
|
|
294
|
+
##
|
295
|
+
# Converts temperature to Celsius scale. If temperature is already in Celsius, returns current
|
296
|
+
# temperature object.
|
297
|
+
#
|
298
|
+
# Memoizes subsequent calls.
|
299
|
+
#
|
300
|
+
# Conversion formulas are taken from {RapidTables}[https://www.rapidtables.com/]:
|
301
|
+
# 1. {Celsius to Fahrenheit}[https://www.rapidtables.com/convert/temperature/celsius-to-fahrenheit.html].
|
302
|
+
# 2. {Celsius to Kelvin}[https://www.rapidtables.com/convert/temperature/celsius-to-kelvin.html].
|
303
|
+
# 3. {Celsius to Rankine}[https://www.rapidtables.com/convert/temperature/celsius-to-rankine.html].
|
304
|
+
#
|
305
|
+
# Temperature[0, :fahrenheit].to_celsius
|
306
|
+
# # => -17.78 °C
|
307
|
+
#
|
60
308
|
def to_celsius
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
309
|
+
memoized(:to_celsius) || memoize(:to_celsius, -> {
|
310
|
+
return self if self.scale == CELSIUS
|
311
|
+
|
312
|
+
degrees =
|
313
|
+
case self.scale
|
314
|
+
when FAHRENHEIT
|
315
|
+
(self.degrees - 32) * (5 / 9r)
|
316
|
+
when KELVIN
|
317
|
+
self.degrees - 273.15
|
318
|
+
when RANKINE
|
319
|
+
(self.degrees - 491.67) * (5 / 9r)
|
320
|
+
end
|
321
|
+
|
322
|
+
BasicTemperature.new(degrees, CELSIUS)
|
323
|
+
})
|
74
324
|
end
|
75
325
|
|
326
|
+
##
|
327
|
+
# Converts temperature to Fahrenheit scale. If temperature is already in Fahrenheit, returns current
|
328
|
+
# temperature object.
|
329
|
+
#
|
330
|
+
# Memoizes subsequent calls.
|
331
|
+
#
|
332
|
+
# Conversion formulas are taken from {RapidTables}[https://www.rapidtables.com/]:
|
333
|
+
# 1. {Fahrenheit to Celsius}[https://www.rapidtables.com/convert/temperature/fahrenheit-to-celsius.html].
|
334
|
+
# 2. {Fahrenheit to Kelvin}[https://www.rapidtables.com/convert/temperature/fahrenheit-to-kelvin.html].
|
335
|
+
# 3. {Fahrenheit to Rankine}[https://www.rapidtables.com/convert/temperature/fahrenheit-to-rankine.html].
|
336
|
+
#
|
337
|
+
# Temperature[0, :celsius].to_fahrenheit
|
338
|
+
# # => 32 °F
|
339
|
+
#
|
76
340
|
def to_fahrenheit
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
341
|
+
memoized(:to_fahrenheit) || memoize(:to_fahrenheit, -> {
|
342
|
+
return self if self.scale == FAHRENHEIT
|
343
|
+
|
344
|
+
degrees =
|
345
|
+
case self.scale
|
346
|
+
when CELSIUS
|
347
|
+
self.degrees * (9 / 5r) + 32
|
348
|
+
when KELVIN
|
349
|
+
self.degrees * (9 / 5r) - 459.67
|
350
|
+
when RANKINE
|
351
|
+
self.degrees - 459.67
|
352
|
+
end
|
353
|
+
|
354
|
+
BasicTemperature.new(degrees, FAHRENHEIT)
|
355
|
+
})
|
90
356
|
end
|
91
357
|
|
358
|
+
##
|
359
|
+
# Converts temperature to Kelvin scale. If temperature is already in Kelvin, returns current
|
360
|
+
# temperature object.
|
361
|
+
#
|
362
|
+
# Memoizes subsequent calls.
|
363
|
+
#
|
364
|
+
# Conversion formulas are taken from {RapidTables}[https://www.rapidtables.com/]:
|
365
|
+
# 1. {Kelvin to Celsius}[https://www.rapidtables.com/convert/temperature/kelvin-to-celsius.html].
|
366
|
+
# 2. {Kelvin to Fahrenheit}[https://www.rapidtables.com/convert/temperature/kelvin-to-fahrenheit.html].
|
367
|
+
# 3. {Kelvin to Rankine}[https://www.rapidtables.com/convert/temperature/kelvin-to-rankine.html].
|
368
|
+
#
|
369
|
+
# Temperature[0, :kelvin].to_rankine
|
370
|
+
# # => 0 °R
|
371
|
+
#
|
92
372
|
def to_kelvin
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
373
|
+
memoized(:to_kelvin) || memoize(:to_kelvin, -> {
|
374
|
+
return self if self.scale == KELVIN
|
375
|
+
|
376
|
+
degrees =
|
377
|
+
case self.scale
|
378
|
+
when CELSIUS
|
379
|
+
self.degrees + 273.15
|
380
|
+
when FAHRENHEIT
|
381
|
+
(self.degrees + 459.67) * (5 / 9r)
|
382
|
+
when RANKINE
|
383
|
+
self.degrees * (5 / 9r)
|
384
|
+
end
|
385
|
+
|
386
|
+
BasicTemperature.new(degrees, KELVIN)
|
387
|
+
})
|
388
|
+
end
|
104
389
|
|
105
|
-
|
390
|
+
##
|
391
|
+
# Converts temperature to Rankine scale. If temperature is already in Rankine, returns current
|
392
|
+
# temperature object.
|
393
|
+
#
|
394
|
+
# Memoizes subsequent calls.
|
395
|
+
#
|
396
|
+
# Conversion formulas are taken from {RapidTables}[https://www.rapidtables.com/]:
|
397
|
+
# 1. {Rankine to Celsius}[https://www.rapidtables.com/convert/temperature/rankine-to-celsius.html].
|
398
|
+
# 2. {Rankine to Fahrenheit}[https://www.rapidtables.com/convert/temperature/rankine-to-fahrenheit.html].
|
399
|
+
# 3. {Rankine to Kelvin}[https://www.rapidtables.com/convert/temperature/rankine-to-kelvin.html].
|
400
|
+
#
|
401
|
+
# Temperature[0, :rankine].to_kelvin
|
402
|
+
# # => 0 K
|
403
|
+
#
|
404
|
+
def to_rankine
|
405
|
+
memoized(:to_rankine) || memoize(:to_rankine, -> {
|
406
|
+
return self if self.scale == RANKINE
|
407
|
+
|
408
|
+
degrees =
|
409
|
+
case self.scale
|
410
|
+
when CELSIUS
|
411
|
+
(self.degrees + 273.15) * (9 / 5r)
|
412
|
+
when FAHRENHEIT
|
413
|
+
self.degrees + 459.67
|
414
|
+
when KELVIN
|
415
|
+
self.degrees * (9 / 5r)
|
416
|
+
end
|
417
|
+
|
418
|
+
BasicTemperature.new(degrees, RANKINE)
|
419
|
+
})
|
106
420
|
end
|
107
421
|
|
108
|
-
|
109
|
-
|
422
|
+
##
|
423
|
+
# Compares temperture with <tt>other</tt> temperature.
|
424
|
+
#
|
425
|
+
# Returns <tt>0</tt> if they are considered as equal.
|
426
|
+
#
|
427
|
+
# Two temperatures are considered as equal when they have the same amount of <tt>degrees</tt>.
|
428
|
+
#
|
429
|
+
# Returns <tt>-1</tt> if temperature is lower than <tt>other</tt> temperature.
|
430
|
+
#
|
431
|
+
# Returns <tt>1</tt> if temperature is higher than <tt>other</tt> temperature.
|
432
|
+
#
|
433
|
+
# If <tt>other</tt> temperature has a different scale, temperature is automatically converted to that scale
|
434
|
+
# before <tt>degrees</tt> comparison.
|
435
|
+
#
|
436
|
+
# Temperature[20, :celsius] <=> Temperature[20, :celsius]
|
437
|
+
# # => 0
|
438
|
+
#
|
439
|
+
# Temperature[20, :celsius] <=> Temperature[293.15, :kelvin]
|
440
|
+
# # => 0
|
441
|
+
#
|
442
|
+
# IMPORTANT!!!
|
443
|
+
#
|
444
|
+
# This method rounds <tt>degrees</tt> to the nearest value with a precision of 2 decimal digits.
|
445
|
+
#
|
446
|
+
# This means the following:
|
447
|
+
#
|
448
|
+
# Temperature[20.020, :celsius] <=> Temperature[20.024, :celsius]
|
449
|
+
# # => 0
|
450
|
+
#
|
451
|
+
# Temperature[20.025, :celsius] <=> Temperature[20.029, :celsius]
|
452
|
+
# # => 0
|
453
|
+
#
|
454
|
+
# Temperature[20.024, :celsius] <=> Temperature[20.029, :celsius]
|
455
|
+
# # => -1
|
456
|
+
#
|
457
|
+
def <=>(other)
|
458
|
+
return unless assert_temperature(other)
|
110
459
|
|
111
|
-
self.
|
460
|
+
compare_degrees(self.to_scale(other.scale).degrees, other.degrees)
|
112
461
|
end
|
113
462
|
|
463
|
+
##
|
464
|
+
# Performs addition. Returns a new Temperature.
|
465
|
+
#
|
466
|
+
# Temperature[20, :celsius] + Temperature[10, :celsius]
|
467
|
+
# # => 30 °C
|
468
|
+
#
|
469
|
+
# If the second temperature has a different scale, the first temperature is automatically converted to that
|
470
|
+
# scale before <tt>degrees</tt> addition.
|
471
|
+
#
|
472
|
+
# Temperature[283.15, :kelvin] + Temperature[20, :celsius]
|
473
|
+
# # => 30 °C
|
474
|
+
#
|
475
|
+
# Returned temperature will have the same scale as the second temperature.
|
476
|
+
#
|
477
|
+
# It is possible to add numerics.
|
478
|
+
#
|
479
|
+
# Temperature[20, :celsius] + 10
|
480
|
+
# # => 30 °C
|
481
|
+
#
|
482
|
+
# In such cases, returned temperature will have the same scale as the first temperature.
|
483
|
+
#
|
484
|
+
# Also {Ruby coersion mechanism}[https://ruby-doc.org/core/Numeric.html#method-i-coerce] is supported.
|
485
|
+
#
|
486
|
+
# 10 + Temperature[20, :celsius]
|
487
|
+
# # => 30 °C
|
488
|
+
#
|
489
|
+
# :call-seq:
|
490
|
+
# +(temperature)
|
491
|
+
# +(numeric)
|
492
|
+
#
|
114
493
|
def +(other)
|
494
|
+
assert_numeric_or_temperature!(other)
|
495
|
+
|
115
496
|
degrees, scale =
|
116
497
|
case other
|
117
498
|
when Numeric
|
118
499
|
[self.degrees + other, self.scale]
|
119
500
|
when BasicTemperature
|
120
501
|
[self.to_scale(other.scale).degrees + other.degrees, other.scale]
|
121
|
-
else
|
122
|
-
raise_invalid_other_error(other)
|
123
502
|
end
|
124
503
|
|
125
504
|
BasicTemperature.new(degrees, scale)
|
126
505
|
end
|
127
506
|
|
507
|
+
##
|
508
|
+
# Performs subtraction. Returns a new Temperature.
|
509
|
+
#
|
510
|
+
# Temperature[20, :celsius] - Temperature[10, :celsius]
|
511
|
+
# # => 10 °C
|
512
|
+
#
|
513
|
+
# If the second temperature has a different scale, the first temperature is automatically converted to that
|
514
|
+
# scale before <tt>degrees</tt> subtraction.
|
515
|
+
#
|
516
|
+
# Temperature[283.15, :kelvin] + Temperature[10, :celsius]
|
517
|
+
# # => 10 °C
|
518
|
+
#
|
519
|
+
# Returned temperature will have the same scale as the second temperature.
|
520
|
+
#
|
521
|
+
# It is possible to subtract numerics.
|
522
|
+
#
|
523
|
+
# Temperature[20, :celsius] - 10
|
524
|
+
# # => 10 °C
|
525
|
+
#
|
526
|
+
# In such cases, returned temperature will have the same scale as the first temperature.
|
527
|
+
#
|
528
|
+
# Also {Ruby coersion mechanism}[https://ruby-doc.org/core/Numeric.html#method-i-coerce] is supported.
|
529
|
+
#
|
530
|
+
# 10 - Temperature[20, :celsius]
|
531
|
+
# # => -10 °C
|
532
|
+
#
|
533
|
+
# :call-seq:
|
534
|
+
# -(temperature)
|
535
|
+
# -(numeric)
|
536
|
+
#
|
128
537
|
def -(other)
|
129
538
|
self + -other
|
130
539
|
end
|
131
540
|
|
541
|
+
##
|
542
|
+
# Returns a new Temperature with negated <tt>degrees</tt>.
|
543
|
+
#
|
544
|
+
# -Temperature[20, :celsius]
|
545
|
+
# # => -20 °C
|
546
|
+
#
|
132
547
|
def -@
|
133
548
|
BasicTemperature.new(-self.degrees, self.scale)
|
134
549
|
end
|
135
550
|
|
136
|
-
|
551
|
+
# Is used by {+}[rdoc-ref:BasicTemperature#+] and {-}[rdoc-ref:BasicTemperature#-]
|
552
|
+
# for {Ruby coersion mechanism}[https://ruby-doc.org/core/Numeric.html#method-i-coerce].
|
553
|
+
def coerce(numeric) #:nodoc:
|
137
554
|
assert_numeric!(numeric)
|
138
555
|
|
139
556
|
[BasicTemperature.new(numeric, self.scale), self]
|
140
557
|
end
|
141
558
|
|
142
|
-
|
143
|
-
|
144
|
-
|
559
|
+
# Returns a string containing a human-readable representation of temperature.
|
560
|
+
def inspect #:nodoc:
|
561
|
+
rounded_degrees = round_degrees(degrees)
|
145
562
|
|
146
|
-
|
147
|
-
return unless other.instance_of?(BasicTemperature)
|
563
|
+
printable_degrees = degrees_without_decimal?(rounded_degrees) ? rounded_degrees.to_i : rounded_degrees
|
148
564
|
|
149
|
-
|
565
|
+
scale_symbol =
|
566
|
+
case self.scale
|
567
|
+
when CELSIUS
|
568
|
+
'°C'
|
569
|
+
when FAHRENHEIT
|
570
|
+
'°F'
|
571
|
+
when KELVIN
|
572
|
+
'K'
|
573
|
+
when RANKINE
|
574
|
+
'°R'
|
575
|
+
end
|
576
|
+
|
577
|
+
"#{printable_degrees} #{scale_symbol}"
|
150
578
|
end
|
151
579
|
|
152
580
|
private
|
153
581
|
|
582
|
+
# Initialization
|
154
583
|
def initialize_via_positional_arguments(positional_arguments)
|
155
584
|
degrees, scale = positional_arguments
|
156
585
|
|
@@ -164,19 +593,31 @@ class BasicTemperature
|
|
164
593
|
end
|
165
594
|
|
166
595
|
def initialize_arguments(degrees, scale)
|
596
|
+
casted_degrees = cast_degrees(degrees)
|
167
597
|
casted_scale = cast_scale(scale)
|
168
598
|
|
169
|
-
assert_valid_degrees!(
|
599
|
+
assert_valid_degrees!(casted_degrees)
|
170
600
|
assert_valid_scale!(casted_scale)
|
171
601
|
|
172
|
-
@degrees =
|
602
|
+
@degrees = casted_degrees
|
173
603
|
@scale = casted_scale
|
174
604
|
end
|
175
605
|
|
606
|
+
# Casting
|
607
|
+
def cast_degrees(degrees)
|
608
|
+
Float(degrees) rescue nil
|
609
|
+
end
|
610
|
+
|
176
611
|
def cast_scale(scale)
|
177
612
|
scale.to_s
|
178
613
|
end
|
179
614
|
|
615
|
+
# Assertions
|
616
|
+
def assert_either_positional_arguments_or_keyword_arguments!(positional_arguments, keyword_arguments)
|
617
|
+
raise_initialization_arguments_error if positional_arguments.any? && keyword_arguments.any?
|
618
|
+
raise_initialization_arguments_error if positional_arguments.none? && keyword_arguments.none?
|
619
|
+
end
|
620
|
+
|
180
621
|
def assert_valid_degrees!(degrees)
|
181
622
|
raise_invalid_degrees_error unless degrees.is_a?(Numeric)
|
182
623
|
end
|
@@ -188,13 +629,18 @@ class BasicTemperature
|
|
188
629
|
def assert_numeric_or_temperature!(numeric_or_temperature)
|
189
630
|
return if numeric_or_temperature.is_a?(Numeric) || numeric_or_temperature.instance_of?(BasicTemperature)
|
190
631
|
|
191
|
-
|
632
|
+
raise_invalid_numeric_or_temperature_error(numeric_or_temperature)
|
192
633
|
end
|
193
634
|
|
194
635
|
def assert_numeric!(numeric)
|
195
636
|
raise_invalid_numeric unless numeric.is_a?(Numeric)
|
196
637
|
end
|
197
638
|
|
639
|
+
def assert_temperature(temperature)
|
640
|
+
temperature.instance_of?(BasicTemperature)
|
641
|
+
end
|
642
|
+
|
643
|
+
# Raising errors
|
198
644
|
def raise_initialization_arguments_error
|
199
645
|
message =
|
200
646
|
'Positional and keyword arguments are mixed or ' \
|
@@ -215,12 +661,43 @@ class BasicTemperature
|
|
215
661
|
raise InvalidScaleError, message
|
216
662
|
end
|
217
663
|
|
218
|
-
def
|
219
|
-
raise
|
664
|
+
def raise_invalid_numeric_or_temperature_error(numeric_or_temperature)
|
665
|
+
raise InvalidNumericOrTemperatureError, "`#{numeric_or_temperature}` is neither Numeric nor Temperature."
|
666
|
+
end
|
667
|
+
|
668
|
+
# Rounding
|
669
|
+
def round_degrees(degrees)
|
670
|
+
degrees.round(2)
|
671
|
+
end
|
672
|
+
|
673
|
+
def compare_degrees(first_degrees, second_degrees)
|
674
|
+
round_degrees(first_degrees) <=> round_degrees(second_degrees)
|
675
|
+
end
|
676
|
+
|
677
|
+
def degrees_with_decimal?(degrees)
|
678
|
+
degrees % 1 != 0
|
679
|
+
end
|
680
|
+
|
681
|
+
def degrees_without_decimal?(degrees)
|
682
|
+
!degrees_with_decimal?(degrees)
|
683
|
+
end
|
684
|
+
|
685
|
+
# Memoization
|
686
|
+
def memoized(key)
|
687
|
+
name = convert_to_variable_name(key)
|
688
|
+
|
689
|
+
instance_variable_get(name) if instance_variable_defined?(name)
|
690
|
+
end
|
691
|
+
|
692
|
+
def memoize(key, proc)
|
693
|
+
name = convert_to_variable_name(key)
|
694
|
+
value = proc.call
|
695
|
+
|
696
|
+
instance_variable_set(name, value)
|
220
697
|
end
|
221
698
|
|
222
|
-
def
|
223
|
-
|
699
|
+
def convert_to_variable_name(key)
|
700
|
+
"@#{key}"
|
224
701
|
end
|
225
702
|
end
|
226
703
|
# rubocop:enable Metrics/ClassLength
|