amee-data-abstraction 1.0.0
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.
- 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
|