amee-data-abstraction 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/CHANGELOG.txt +4 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +27 -0
- data/README.txt +188 -0
- data/Rakefile +102 -0
- data/VERSION +1 -0
- data/amee-data-abstraction.gemspec +115 -0
- data/examples/_calculator_form.erb +27 -0
- data/examples/calculation_controller.rb +16 -0
- data/init.rb +4 -0
- data/lib/amee-data-abstraction.rb +30 -0
- data/lib/amee-data-abstraction/calculation.rb +236 -0
- data/lib/amee-data-abstraction/calculation_set.rb +101 -0
- data/lib/amee-data-abstraction/drill.rb +63 -0
- data/lib/amee-data-abstraction/exceptions.rb +47 -0
- data/lib/amee-data-abstraction/input.rb +197 -0
- data/lib/amee-data-abstraction/metadatum.rb +58 -0
- data/lib/amee-data-abstraction/ongoing_calculation.rb +545 -0
- data/lib/amee-data-abstraction/output.rb +16 -0
- data/lib/amee-data-abstraction/profile.rb +108 -0
- data/lib/amee-data-abstraction/prototype_calculation.rb +350 -0
- data/lib/amee-data-abstraction/term.rb +506 -0
- data/lib/amee-data-abstraction/terms_list.rb +150 -0
- data/lib/amee-data-abstraction/usage.rb +90 -0
- data/lib/config/amee_units.rb +129 -0
- data/lib/core-extensions/class.rb +27 -0
- data/lib/core-extensions/hash.rb +43 -0
- data/lib/core-extensions/ordered_hash.rb +21 -0
- data/lib/core-extensions/proc.rb +15 -0
- data/rails/init.rb +32 -0
- data/spec/amee-data-abstraction/calculation_set_spec.rb +54 -0
- data/spec/amee-data-abstraction/calculation_spec.rb +75 -0
- data/spec/amee-data-abstraction/drill_spec.rb +38 -0
- data/spec/amee-data-abstraction/input_spec.rb +77 -0
- data/spec/amee-data-abstraction/metadatum_spec.rb +17 -0
- data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +494 -0
- data/spec/amee-data-abstraction/profile_spec.rb +39 -0
- data/spec/amee-data-abstraction/prototype_calculation_spec.rb +256 -0
- data/spec/amee-data-abstraction/term_spec.rb +385 -0
- data/spec/amee-data-abstraction/terms_list_spec.rb +53 -0
- data/spec/config/amee_units_spec.rb +71 -0
- data/spec/core-extensions/class_spec.rb +25 -0
- data/spec/core-extensions/hash_spec.rb +44 -0
- data/spec/core-extensions/ordered_hash_spec.rb +12 -0
- data/spec/core-extensions/proc_spec.rb +12 -0
- data/spec/fixtures/electricity.rb +35 -0
- data/spec/fixtures/electricity_and_transport.rb +55 -0
- data/spec/fixtures/transport.rb +26 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +244 -0
- metadata +262 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: AMEE::DataAbstraction::TermsList
|
5
|
+
|
6
|
+
module AMEE
|
7
|
+
module DataAbstraction
|
8
|
+
|
9
|
+
# Class extending the <i>Array</i> and providing specific attributes and
|
10
|
+
# methods for operating on a collection of instances of the class <i>Term</i>.
|
11
|
+
#
|
12
|
+
class TermsList < Array
|
13
|
+
|
14
|
+
# Subclasses of the <i>Term</i> class which <tt>self</tt> can contain.
|
15
|
+
#
|
16
|
+
# Each subclass symbol also represents a dynamically generated method name
|
17
|
+
# for <tt>self</tt> which can be called to return a new <tt>TermsList</tt>
|
18
|
+
# instance containing that subset of terms only, e.g.,
|
19
|
+
#
|
20
|
+
# my_terms_list.inputs #=> <AMEE::DataAbstraction::TermsList ... >
|
21
|
+
#
|
22
|
+
# my_terms_list.profiles #=> <AMEE::DataAbstraction::TermsList ... >
|
23
|
+
#
|
24
|
+
# These methods can be compounded:
|
25
|
+
#
|
26
|
+
# my_terms_list.inputs.drills #=> <AMEE::DataAbstraction::TermsList ... >
|
27
|
+
#
|
28
|
+
# my_terms_list.profiles.visible #=> <AMEE::DataAbstraction::TermsList ... >
|
29
|
+
#
|
30
|
+
TermClasses= [:profiles,:drills,:inputs,:outputs,:metadata,:usages]
|
31
|
+
|
32
|
+
TermClasses.each do |term|
|
33
|
+
define_method(term) do
|
34
|
+
self.class.new select{|x|x.is_a? AMEE::DataAbstraction::const_get(term.to_s.singularize.classify)}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Boolean attributes of instances of the <i>Term</i> class.
|
39
|
+
#
|
40
|
+
# Each attribute symbol also represents a dynamically generated method name
|
41
|
+
# for <tt>self</tt> which can be called to return a new <tt>TermsList</tt>
|
42
|
+
# instance containing that subset of only those terms for which the attribute
|
43
|
+
# is true, e.g.,
|
44
|
+
#
|
45
|
+
# my_terms_list.visible #=> <AMEE::DataAbstraction::TermsList ... >
|
46
|
+
#
|
47
|
+
# my_terms_list.set #=> <AMEE::DataAbstraction::TermsList ... >
|
48
|
+
#
|
49
|
+
# These methods can be compounded:
|
50
|
+
#
|
51
|
+
# my_terms_list.drills.visible.set #=> <AMEE::DataAbstraction::TermsList ... >
|
52
|
+
#
|
53
|
+
TermFlags=[:set,:unset,:visible,:hidden,:fixed,
|
54
|
+
:optional,:compulsory,:enabled,:disabled,:drop_down,:text_box,:date]
|
55
|
+
|
56
|
+
TermFlags.each do |term|
|
57
|
+
define_method(term) do
|
58
|
+
self.class.new select{|x|x.send("#{term}?".to_sym)}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
63
|
+
# which occur before the term labeled <tt>label</tt> in the owning
|
64
|
+
# calculation
|
65
|
+
#
|
66
|
+
def before(label)
|
67
|
+
self.class.new select{|x|x.before?(label)}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
71
|
+
# which occur after the term labeled <tt>label</tt> in the owning
|
72
|
+
# calculation
|
73
|
+
#
|
74
|
+
def after(label)
|
75
|
+
self.class.new select{|x|x.after?(label)}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
79
|
+
# which are optional in the owning calculation.
|
80
|
+
#
|
81
|
+
# If no argument is provided, the optional status of each term is defined
|
82
|
+
# according to the current usage of the parent caluclation. Otherwise,
|
83
|
+
# optional status is determined on the basis of the usage whose AMEE
|
84
|
+
# platform path matches <tt>usage</tt>
|
85
|
+
#
|
86
|
+
def optional(usage=nil)
|
87
|
+
self.class.new select{|x|x.optional?(usage)}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
91
|
+
# which are compulsory in the owning calculation.
|
92
|
+
#
|
93
|
+
# If no argument is provided, the compulsory status of each term is defined
|
94
|
+
# according to the current usage of the parent caluclation. Otherwise,
|
95
|
+
# compulsory status is determined on the basis of the usage whose AMEE
|
96
|
+
# platform path matches <tt>usage</tt>
|
97
|
+
#
|
98
|
+
def compulsory(usage=nil)
|
99
|
+
self.class.new select{|x|x.compulsory?(usage)}
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
103
|
+
# which are either compulsory OR optional in the owning calculation, i.e.
|
104
|
+
# any which are NOT forbidden.
|
105
|
+
#
|
106
|
+
# If no argument is provided, the optional/compulsory status of each term
|
107
|
+
# is defined according to the current usage of the parent caluclation.
|
108
|
+
# Otherwise, optional/compulsory status is determined on the basis of the
|
109
|
+
# usage whose AMEE platform path matches <tt>usage</tt>
|
110
|
+
#
|
111
|
+
def in_use(usage=nil)
|
112
|
+
self.class.new select{|x|x.in_use?(usage)}
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return a new <tt>TermsList</tt> instance containing that subset of terms
|
116
|
+
# which are neither compulsory OR optional in the owning calculation, i.e.
|
117
|
+
# those which are forbidden.
|
118
|
+
#
|
119
|
+
# If no argument is provided, the forbidden status of each term is defined
|
120
|
+
# according to the current usage of the parent caluclation. Otherwise,
|
121
|
+
# forbidden status is determined on the basis of the usage whose AMEE
|
122
|
+
# platform path matches <tt>usage</tt>
|
123
|
+
#
|
124
|
+
def out_of_use(usage=nil)
|
125
|
+
self.class.new select{|x|x.out_of_use?(usage)}
|
126
|
+
end
|
127
|
+
|
128
|
+
Selectors=TermClasses+TermFlags+[:before,:after,:optional,
|
129
|
+
:compulsory,:in_use,:out_of_use]
|
130
|
+
|
131
|
+
# Attributes of the class <i>Term</tt>.
|
132
|
+
#
|
133
|
+
# Each attribute symbol also defines a dynamically generated method which
|
134
|
+
# return arrays of the values of the named attribute for all terms, e.g.,
|
135
|
+
#
|
136
|
+
# my_terms_list.labels => [ :type, :fuel, :distance, :co2 ... ]
|
137
|
+
#
|
138
|
+
# my_terms_list.values => [ 'van;, 'petrol', 500, 25.4 ... ]
|
139
|
+
#
|
140
|
+
TermProperties=[:label,:name,:path,:value,:unit,:per_unit,:default_unit,:default_per_unit]
|
141
|
+
|
142
|
+
TermProperties.each do |term|
|
143
|
+
define_method(term.to_s.pluralize.to_sym) do
|
144
|
+
map{|x|x.send(term)}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: AMEE::DataAbstraction::Usage
|
5
|
+
|
6
|
+
module AMEE
|
7
|
+
module DataAbstraction
|
8
|
+
|
9
|
+
# Subclass of <tt>Input</tt> providing methods and attributes appropriate for
|
10
|
+
# representing adjustable calculation usages specifically.
|
11
|
+
#
|
12
|
+
# Only one instance of <i>Usage</i> can be assocaited with a particular
|
13
|
+
# calucaltion object. When the value of <tt>self</tt> is changed, profile
|
14
|
+
# item value terms which are forbidden in the new usage will be inactivated
|
15
|
+
# and optional/compulsory flags are set on the remaining terms.
|
16
|
+
#
|
17
|
+
class Usage < Input
|
18
|
+
|
19
|
+
# Initialization of <i>Usage</i> objects follows that of the parent
|
20
|
+
# <i>Input</i> class, with a number of differences.
|
21
|
+
#
|
22
|
+
# If the parent caluclation already contains a usage term, a <i>TwoUsages</i>
|
23
|
+
# exception is raised.
|
24
|
+
#
|
25
|
+
# The <tt>label<tt> attribute is set by default to <tt>:usage</tt>.
|
26
|
+
#
|
27
|
+
# The <tt>interface</tt> attribute of <tt>self</tt> is set to
|
28
|
+
# <tt>:drop_down</tt> by default, but can be manually configured if
|
29
|
+
# required.
|
30
|
+
#
|
31
|
+
# The <tt>inactive</tt> property of <tt>self</tt> is set to <tt>:invisible</tt>
|
32
|
+
# by default.
|
33
|
+
#
|
34
|
+
def initialize(options={},&block)
|
35
|
+
raise Exceptions::TwoUsages if options[:parent].current_usage
|
36
|
+
label :usage
|
37
|
+
@inactive=:invisible
|
38
|
+
super
|
39
|
+
interface :drop_down unless interface
|
40
|
+
end
|
41
|
+
|
42
|
+
# Represents the method of handling forbidden terms. Should they be hidden
|
43
|
+
# in generated UIs or just disabled (greyed out)? Set the behaviour by
|
44
|
+
# passing either <tt>:invisible</tt> or <tt>:disabled</tt> as an argument.
|
45
|
+
# Retrieve the defined behaviour by calling without an argument.
|
46
|
+
#
|
47
|
+
attr_property :inactive
|
48
|
+
|
49
|
+
# Adjust the value of <tt>self</tt> indicating that a new usage should be
|
50
|
+
# switch to in the parent caluclation. This method has the effect of
|
51
|
+
# (de)activating terms in the parent calculation as appropriate.
|
52
|
+
#
|
53
|
+
def value(*args)
|
54
|
+
unless args.empty?
|
55
|
+
@value=args.first
|
56
|
+
activate_selected(value)
|
57
|
+
end
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
# Activate and deactivate terms in the parent calculation according to the
|
62
|
+
# compulsory/optional/forbidden status' of each in the usage indicated by
|
63
|
+
# <tt>usage</tt>
|
64
|
+
#
|
65
|
+
def activate_selected(usage=nil)
|
66
|
+
parent.profiles.in_use(usage).each do |term|
|
67
|
+
case @inactive
|
68
|
+
when :invisible
|
69
|
+
term.show!
|
70
|
+
when :disabled
|
71
|
+
term.enable!
|
72
|
+
end
|
73
|
+
end
|
74
|
+
parent.profiles.out_of_use(usage).each do |term|
|
75
|
+
case @inactive
|
76
|
+
when :invisible
|
77
|
+
term.hide!
|
78
|
+
when :disabled
|
79
|
+
term.disable!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns an array of available valid values for <tt>self</tt>.
|
85
|
+
def choices
|
86
|
+
parent.amee_usages
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
require 'quantify'
|
5
|
+
|
6
|
+
Quantify::Unit.configure do
|
7
|
+
|
8
|
+
# Remove from the system of known units, the units which are not used or not
|
9
|
+
# anticipated to be useful for interacting with AMEE. These either represent
|
10
|
+
# physical quantities which are not typically represented in emissions
|
11
|
+
# calculations (e.g. redioactivity, thermal resistance) or units which do
|
12
|
+
# represent appropriate physical quantities but are relatively obscure or
|
13
|
+
# inappropriate (e.g angstrom, calorie, cup, light year).
|
14
|
+
#
|
15
|
+
# Note: although AMEE supports 8 types of British thermal unit, only one is
|
16
|
+
# made available since it is unlikely that users would like to choose between
|
17
|
+
# each of the alternatives.
|
18
|
+
|
19
|
+
required_units = [ "1",
|
20
|
+
"Hz",
|
21
|
+
"Ohm",
|
22
|
+
"V",
|
23
|
+
"m/s",
|
24
|
+
"m/s²",
|
25
|
+
"A",
|
26
|
+
"K",
|
27
|
+
"m",
|
28
|
+
"mol",
|
29
|
+
"s",
|
30
|
+
"kg",
|
31
|
+
"g",
|
32
|
+
"km",
|
33
|
+
"m³",
|
34
|
+
"J",
|
35
|
+
"N",
|
36
|
+
"W",
|
37
|
+
"Pa",
|
38
|
+
"m²",
|
39
|
+
"atm",
|
40
|
+
"bar",
|
41
|
+
"ha",
|
42
|
+
"L",
|
43
|
+
"bbl",
|
44
|
+
"oz_fl_uk",
|
45
|
+
"gal_uk",
|
46
|
+
"gallon_dry_us",
|
47
|
+
"oz_fl",
|
48
|
+
"bbl_fl_us",
|
49
|
+
"gal",
|
50
|
+
"kWh",
|
51
|
+
"°C",
|
52
|
+
"°F",
|
53
|
+
"°R",
|
54
|
+
"ft",
|
55
|
+
"h",
|
56
|
+
"in",
|
57
|
+
"mi",
|
58
|
+
"min",
|
59
|
+
"month",
|
60
|
+
"nmi",
|
61
|
+
"oz",
|
62
|
+
"lb",
|
63
|
+
"t",
|
64
|
+
"week",
|
65
|
+
"yd",
|
66
|
+
"year",
|
67
|
+
"BTU_FiftyNineF",
|
68
|
+
"ton_us",
|
69
|
+
"ton_uk",
|
70
|
+
"d" ]
|
71
|
+
|
72
|
+
uneeded_units = []
|
73
|
+
|
74
|
+
units.each do |unit|
|
75
|
+
uneeded_units << unit.label unless required_units.include?(unit.label)
|
76
|
+
end
|
77
|
+
|
78
|
+
unload(uneeded_units)
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
Quantify::Unit::SI.configure do
|
83
|
+
|
84
|
+
# Load the commonly used SI unit/prefix combinations which are used in AMEE
|
85
|
+
prefix_and_load [:mega,:giga], :gram
|
86
|
+
prefix_and_load [:mega,:giga,:tera], :joule
|
87
|
+
prefix_and_load [:kilo,:mega], :watt
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
Quantify::Unit::Prefix::NonSI.configure do
|
92
|
+
|
93
|
+
# define the nonconventional 'M' (million) prefix which is used with British
|
94
|
+
# thermal units. This is similar, but distinct to the SI mega- prefix
|
95
|
+
load :name => 'million ', :symbol => 'M', :factor => 1e6
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
Quantify::Unit::NonSI.configure do
|
100
|
+
|
101
|
+
# Give the remaining (default) British thermal unit version a humanised name
|
102
|
+
# and symbol
|
103
|
+
Unit.BTU_FiftyNineF.configure_as_canonical do |unit|
|
104
|
+
unit.name = 'british thermal unit'
|
105
|
+
unit.symbol = 'BTU'
|
106
|
+
end
|
107
|
+
|
108
|
+
# Differentiate long and short ton symbols
|
109
|
+
Unit.ton_us.configure_as_canonical do |unit|
|
110
|
+
unit.symbol = 'ton (US)'
|
111
|
+
end
|
112
|
+
|
113
|
+
Unit.ton_uk.configure_as_canonical do |unit|
|
114
|
+
unit.symbol = 'ton (UK)'
|
115
|
+
end
|
116
|
+
|
117
|
+
# AMEE uses 'day' rather than 'd' as the label for the time period, day
|
118
|
+
Unit.day.canonical_label = 'day'
|
119
|
+
|
120
|
+
# Load prefixed version of British thermal unit, which is used in AMEE
|
121
|
+
prefix_and_load :M, :BTU_FiftyNineF
|
122
|
+
|
123
|
+
# Define and load the megawatt hour, an energy unit used in AMEE
|
124
|
+
construct_and_load(megawatt*hour) do |unit|
|
125
|
+
unit.symbol = 'MWh'
|
126
|
+
unit.label = 'MWh'
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: Class
|
5
|
+
|
6
|
+
class Class
|
7
|
+
|
8
|
+
# Syntactic sugar for providing instance level attributes. Similar to
|
9
|
+
# <tt>attr_accessor</tt> except the value of property is set without requiring
|
10
|
+
# "=" syntax, e.g.
|
11
|
+
#
|
12
|
+
# foo.propname 5 (rather than <tt>foo.attrname=5</tt>)
|
13
|
+
#
|
14
|
+
# foo.propname #=> 5
|
15
|
+
#
|
16
|
+
# In other words, setting is performed by specifing an argument, getting is
|
17
|
+
# performed using the same method call without an argument.
|
18
|
+
#
|
19
|
+
def attr_property(*accessors)
|
20
|
+
accessors.each do |m|
|
21
|
+
define_method(m) do |*val|
|
22
|
+
instance_variable_set("@#{m}",val.first) unless val.empty? #Array Hack to avoid warning
|
23
|
+
instance_variable_get("@#{m}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: Hash
|
5
|
+
|
6
|
+
class Hash
|
7
|
+
|
8
|
+
# Return a new instance of <i>Hash</i> which represents the same data as
|
9
|
+
# <tt>self</tt> but with all keys - including those of sub-hashes - symbolized
|
10
|
+
#
|
11
|
+
def recursive_symbolize_keys
|
12
|
+
new_hash = {}
|
13
|
+
self.each_pair do |k,v|
|
14
|
+
new_hash[k.to_sym] = value_or_symbolize_value(v)
|
15
|
+
end
|
16
|
+
new_hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# Modify <tt>self</tt> in place, transforming all keys - including those of
|
20
|
+
# sub-hashes - in to symbols
|
21
|
+
#
|
22
|
+
def recursive_symbolize_keys!
|
23
|
+
self.each_pair do |k,v|
|
24
|
+
value = delete(k)
|
25
|
+
self[k.to_sym] = value_or_symbolize_value(value)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Symbolize any hash key or sub-hash key containing within <tt>value</tt>.
|
33
|
+
def value_or_symbolize_value(value)
|
34
|
+
if value.is_a? Hash
|
35
|
+
return value.recursive_symbolize_keys
|
36
|
+
elsif value.is_a? Array
|
37
|
+
return value.map { |elem| value_or_symbolize_value(elem) }
|
38
|
+
else
|
39
|
+
return value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: OrderedHash
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
class OrderedHash
|
8
|
+
|
9
|
+
# Version of enumerable#select for an OrderedHash which is order-preserving
|
10
|
+
# Output is an array of key-value pairs.
|
11
|
+
def stable_select(&block)
|
12
|
+
#Annoyingly, default ordered hash select is not stable
|
13
|
+
self.map{|k,v| block.call(k,v) ? [k,v] : nil}.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
# Insert a given element at the beginning, not end, of an ordered hash.
|
17
|
+
def insert_at_start(key,value)
|
18
|
+
replace(OrderedHash[self.to_a.insert(0,[key,value])])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|