-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathbase_properties.rb
More file actions
180 lines (150 loc) · 6.44 KB
/
base_properties.rb
File metadata and controls
180 lines (150 loc) · 6.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
#require 'active_support/hash_with_indifferent_access'
require "active_support"
require 'active_support/core_ext/date/calculations'
module IB
# Module adds prop Macro and
module BaseProperties
extend ActiveSupport::Concern
### Instance methods
# Default presentation
def to_human
"<#{self.class.to_s.demodulize}: " + attributes.map do |attr, value|
"#{attr}: #{value}" unless value.nil?
end.compact.sort.join(' ') + ">"
end
def table_header
[ self.class.to_s.demodulize ] + content_attributes.keys
end
def table_row
[ self.class.to_s.demodulize ] + content_attributes.values
end
# the optional block specifies a title
# i.e.
#s = Symbols::Spreads.stoxx_dez
# puts s.portfolio_value( U).as_table{ s.description[1..-2] }
#┌───────────┬─────────────────────────────────────────────┬─────┬────────┬─────────┬──────────┬────────────┬──────────┐
#│ │ Straddle ESTX50(4200.0)[Dec 2021] │ pos │ entry │ market │ value │ unrealized │ realized │
#╞═══════════╪═════════════════════════════════════════════╪═════╪════════╪═════════╪══════════╪════════════╪══════════╡
#│ U7274612 │ Option: ESTX50 20211217 put 4200.0 DTB EUR │ -4 │ 179.85 │ 169.831 │ -6793.22 │ 400.78 │ │
#│ U7274612 │ Option: ESTX50 20211217 call 4200.0 DTB EUR │ -4 │ 97.85 │ 131.438 │ -5257.51 │ -1343.51 │ │
#└───────────┴─────────────────────────────────────────────┴─────┴────────┴─────────┴──────────┴────────────┴──────────┘
##
def as_table &b
Terminal::Table.new headings: table_header(&b), rows: [table_row ], style: { border: :unicode }
end
# Comparison support
def content_attributes
#NoMethodError if a Hash is assigned to an attribute
Hash[attributes.reject do |(attr, _)|
attr.to_s =~ /(_count)\z/ ||
[:created_at, :type, :updated_at,
:id, :order_id, :contract_id].include?(attr.to_sym)
end]
end
=begin
Remove all Time-Stamps from the list of Attributes
=end
def invariant_attributes
attributes.reject{|x| x =~ /_at/}
end
# Update nil attributes from given Hash or model
def update_missing attrs
attrs = attrs.content_attributes unless attrs.kind_of?(Hash)
attrs.each { |attr, val| send "#{attr}=", val if send(attr).blank? }
self # for chaining
end
# Default Model comparison
def == other
case other
when String # Probably a Rails URI, delegate to AR::Base
super(other)
else
content_attributes.keys.inject(true) { |res, key|
res && other.respond_to?(key) && (send(key) == other.send(key)) }
end
end
### Default attributes support
def default_attributes
{:created_at => Time.now
# :updated_at => Time.now,
}
end
def set_attribute_defaults
default_attributes.each do |key, val|
self.send("#{key}=", val) if self.send(key).nil?
# self.send("#{key}=", val) if self[key].nil? # Problems with association defaults
end
end
included do
after_initialize :set_attribute_defaults
### Class macros
def self.prop *properties
prop_hash = properties.last.is_a?(Hash) ? properties.pop : {}
properties.each { |names| define_property names, nil }
prop_hash.each { |names, type| define_property names, type }
end
def self.define_property names, body
aliases = [names].flatten
name = aliases.shift
instance_eval do
define_property_methods name, body
aliases.each do |ali|
alias_method "#{ali}", name
alias_method "#{ali}=", "#{name}="
end
end
end
def self.define_property_methods name, body={}
#p name, body
case body
when '' # default getter and setter
define_property_methods name
when Array # [setter, getter, validators]
define_property_methods name,
:get => body[0],
:set => body[1],
:validate => body[2]
when Hash # recursion base case
getter = case # Define getter
when body[:get].respond_to?(:call)
body[:get]
when body[:get]
proc { self[name].send "to_#{body[:get]}" }
when IB::VALUES[name] # property is encoded
proc { IB::VALUES[name][self[name]] }
else
proc { self[name] }
end
define_method name, &getter if getter
setter = case # Define setter
when body[:set].respond_to?(:call)
body[:set]
when body[:set]
proc { |value| self[name] = value.send "to_#{body[:set]}" }
when CODES[name] # property is encoded
proc { |value| self[name] = CODES[name][value] || value }
else
proc { |value| self[name] = value } # p name, value;
end
define_method "#{name}=", &setter if setter
# Define validator(s)
[body[:validate]].flatten.compact.each do |validator|
case validator
when Proc
validates_each name, &validator
when Hash
validates name, validator.dup
end
end
# TODO define self[:name] accessors for :virtual and :flag properties
else # setter given
define_property_methods name, :set => body, :get => body
end
end
# Timestamps in lightweight models
# unless defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
prop :created_at #, :updated_at
# end
end # included
end # module BaseProperties
end