Class: RVGP::Journal::ComplexCommodity
- Inherits:
-
Object
- Object
- RVGP::Journal::ComplexCommodity
- Defined in:
- lib/rvgp/journal/complex_commodity.rb
Overview
These ‘complex currency’ specifications appear to be mostly for non-register and non-balance reports. The ledger manual labels these ‘Cost Expressions’. We really don’t use these much, and I’m not entirely sure the parsing rules make sense. Some of the rules in the documentation even seem a bit inconsistent (compare complex expressions vs comments).
Here’s some examples of Complex Commodities: “‘ 10 AAPL @@ $500.00 10 AAPL @ ($500.00 / 10) (5 AAPL * 2) @ ($500.00 / 10) 1000 AAPL (@) $1 -10 AAPL {$500{$500.00} @@ $750.00 10 AAPL =$50=$50.00 -5 AAPL $50$50.00 [2012-04-10] @@ $375.00 -5 AAPL $50$50.00 [2012-04-10] (Oh my!) @@ $375.00 -5 AAPL $50$50.00 ((ten_dollars)) @@ $375.00 -5 AAPL $50$50.00 ((s, d, t -> market($10, date, t))) @@ $375.00 “`
We ended up needing most of this class to run Validations::DuplicateTagsValidation. And, to ensure that we’re able to mostly-validate the syntax of the journals. We don’t actually use many code paths here, otherwise. (Though we do use it to serialize currency conversion in the file shorthand/investment.rb)
I’m not entirely sure what attribute names to use. We could go with intent position, or with class. Either path seems to introduce exceptions. Possibly some of these attributes should just go into the transfer class. I’m also not sure that the left_/right_/operation system makes sense.
I also think we need some adjustments here to cover all parsing cases. But, for now this works well enough, again mostly because we’re not using most of these code paths… Lets see if/how this evolves.
Defined Under Namespace
Classes: Error
Instance Attribute Summary collapse
-
#left ⇒ RVGP::Commodity::Journal
readonly
The ‘left’ component of the complex commodity.
-
#left_date ⇒ Date
readonly
The ‘left_date’ component of the complex commodity.
-
#left_expression ⇒ String
readonly
The ‘left_expression’ component of the complex commodity.
-
#left_is_equal ⇒ TrueClass, FalseClass
readonly
The ‘left_is_equal’ component of the complex commodity.
-
#left_lambda ⇒ String
readonly
The ‘left_lambda’ component of the complex commodity.
-
#left_lot ⇒ String
readonly
The ‘left_lot’ component of the complex commodity.
-
#left_lot_is_equal ⇒ TrueClass, FalseClass
readonly
The ‘left_lot_is_equal’ component of the complex commodity.
-
#left_lot_operation ⇒ Symbol
readonly
The ‘left_lot_operation’ component of the complex commodity, either :per_unit, or :per_lot.
-
#operation ⇒ Symbol
readonly
The ‘operation’ component of the complex commodity, either :right_expression, :left_expression, :per_unit, or :per_lot.
-
#right ⇒ RVGP::Commodity::Journal
readonly
The ‘right’ component of the complex commodity.
-
#right_expression ⇒ String
readonly
The ‘right_expression’ component of the complex commodity.
-
#right_is_equal ⇒ TrueClass, FalseClass
readonly
The ‘right_is_equal’ component of the complex commodity.
Class Method Summary collapse
-
.ensure_not_too_many!(opts, key, string) ⇒ Object
This mostly just saves us some typing above in the from_s().
-
.from_s(string) ⇒ RVGP::Journal::ComplexCommodity
Given a string, in one of the supported formats, construct and return a commodity representation.
Instance Method Summary collapse
-
#initialize(opts = {}) ⇒ ComplexCommodity
constructor
Create a complex commodity, from constituent parts.
-
#invert! ⇒ Object
For now, we simply delegate this message to the :left commodity.
-
#positive? ⇒ Boolean
For now, we simply delegate these messages to the :left commodity.
-
#to_s ⇒ String
De-parse this ComplexCommodity, back into its string representation.
Constructor Details
#initialize(opts = {}) ⇒ ComplexCommodity
Create a complex commodity, from constituent parts
100 101 102 103 104 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 100 def initialize(opts = {}) ATTRIBUTES.select { |attr| opts.key? attr }.each do |attr| instance_variable_set("@#{attr}".to_sym, opts[attr]) end end |
Instance Attribute Details
#left ⇒ RVGP::Commodity::Journal (readonly)
The ‘left’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left @left end |
#left_date ⇒ Date (readonly)
The ‘left_date’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_date @left_date end |
#left_expression ⇒ String (readonly)
The ‘left_expression’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_expression @left_expression end |
#left_is_equal ⇒ TrueClass, FalseClass (readonly)
The ‘left_is_equal’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_is_equal @left_is_equal end |
#left_lambda ⇒ String (readonly)
The ‘left_lambda’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_lambda @left_lambda end |
#left_lot ⇒ String (readonly)
The ‘left_lot’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_lot @left_lot end |
#left_lot_is_equal ⇒ TrueClass, FalseClass (readonly)
The ‘left_lot_is_equal’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_lot_is_equal @left_lot_is_equal end |
#left_lot_operation ⇒ Symbol (readonly)
The ‘left_lot_operation’ component of the complex commodity, either :per_unit, or :per_lot
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def left_lot_operation @left_lot_operation end |
#operation ⇒ Symbol (readonly)
The ‘operation’ component of the complex commodity, either :right_expression, :left_expression, :per_unit, or :per_lot
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def operation @operation end |
#right ⇒ RVGP::Commodity::Journal (readonly)
The ‘right’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def right @right end |
#right_expression ⇒ String (readonly)
The ‘right_expression’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def right_expression @right_expression end |
#right_is_equal ⇒ TrueClass, FalseClass (readonly)
The ‘right_is_equal’ component of the complex commodity
54 55 56 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 54 def right_is_equal @right_is_equal end |
Class Method Details
.ensure_not_too_many!(opts, key, string) ⇒ Object
This mostly just saves us some typing above in the from_s()
209 210 211 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 209 def self.ensure_not_too_many!(opts, key, string) raise Error, format(MSG_TOO_MANY, key.to_s, string) if opts.key? key end |
.from_s(string) ⇒ RVGP::Journal::ComplexCommodity
Given a string, in one of the supported formats, construct and return a commodity representation.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 144 def self.from_s(string) tmp = string.dup opts = {} # We treat string like a kind of stack, and we pop off what we can parse # from the left, heading to the right until tmp.empty? case tmp when WHITESPACE_MATCH tmp = ::Regexp.last_match(1) when EQUAL_MATCH side = opts[:operation] ? :right_is_equal : :left_is_equal ensure_not_too_many! opts, side, string opts[side] = true tmp = ::Regexp.last_match(1) when LOT_MATCH ensure_not_too_many! opts, :left_lot, string opts[:left_lot_operation] = to_operator ::Regexp.last_match(1) opts[:left_lot_is_equal] = (::Regexp.last_match(2) == '=') opts[:left_lot] = ::Regexp.last_match(3).to_commodity tmp = ::Regexp.last_match(4) when LAMBDA_MATCH ensure_not_too_many! opts, :left_lambda, string opts[:left_lambda] = ::Regexp.last_match(1) tmp = ::Regexp.last_match(2) when DATE_MATCH ensure_not_too_many! opts, :left_date, string opts[:left_date] = Date.new(*(1..3).map { |i| ::Regexp.last_match(i).to_i }) tmp = ::Regexp.last_match(4) when OP_MATCH ensure_not_too_many! opts, :operation, string opts[:operation] = to_operator ::Regexp.last_match(1) tmp = ::Regexp.last_match(2) when COMMENT_MATCH side = opts[:operation] ? :right_expression : :left_expression ensure_not_too_many! opts, side, string opts[side] = ::Regexp.last_match(1) tmp = ::Regexp.last_match(2) else begin commodity, tmp = RVGP::Journal::Commodity.from_s_with_remainder tmp rescue RVGP::Journal::Commodity::Error raise Error, MSG_UNPARSEABLE % string end side = opts[:operation] ? :right : :left ensure_not_too_many! opts, side, string opts[side] = commodity end end new opts end |
Instance Method Details
#invert! ⇒ Object
For now, we simply delegate this message to the :left commodity.
116 117 118 119 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 116 def invert! left.invert! self end |
#positive? ⇒ Boolean
For now, we simply delegate these messages to the :left commodity. This path is only in use in the reconciler, which, determines whether the commodity is income or expense. (and/or inverts the amount) It’s conceivable that we may want to test the right commodity at some point here, and determine if the net operation is positive or negative.
111 112 113 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 111 def positive? left.positive? end |
#to_s ⇒ String
De-parse this ComplexCommodity, back into its string representation
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/rvgp/journal/complex_commodity.rb', line 123 def to_s [left_is_equal ? '=' : nil, left ? left.to_s : nil, if left_lot_operation && left_lot format(left_lot_operation == :per_unit ? '{%s}' : '{{%s}}', *[left_lot_is_equal ? '=' : nil, left_lot.to_s].compact.join) end, left_date ? format('[%s]', left_date.to_s) : nil, left_expression ? format('(%s)', left_expression.to_s) : nil, left_lambda ? format('((%s))', left_lambda.to_s) : nil, if operation operation == :per_unit ? '@' : '@@' end, right_is_equal ? '=' : nil, right ? right.to_s : nil, right_expression ? format('(%s)', right_expression.to_s) : nil].compact.join(' ') end |