wherever-positions 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ module DbStore
2
+ module RecordMatcher
3
+ def ==(record)
4
+ self.clean_attributes == record.clean_attributes
5
+ end
6
+
7
+ protected
8
+ def clean_attributes
9
+ att = self.attributes.clone
10
+ att.delete('_id')
11
+ att
12
+ end
13
+
14
+ def clean_attributes_a
15
+ clean_hash(self.attributes)
16
+ end
17
+
18
+ def clean_hash(values)
19
+ res = {}
20
+ values.each do |k, v|
21
+ if k == :_id
22
+ res[k.to_s] = v.is_a?(Hash) ? clean_hash(v) : v
23
+ end
24
+ end
25
+ res
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module DbStore
2
+ class Store
3
+ include Mongoid::Document
4
+ embedded_in :marker, :class_name => 'DbStore::Marker'
5
+ embeds_many :datasets, :class_name => 'DbStore::Dataset'
6
+ embeds_many :identifiers, :class_name => 'DbStore::Identifier'
7
+ field :key, :type => Array
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module DbStore
2
+ end
3
+
4
+ require 'wherever/db_store/record_matcher'
5
+ require 'wherever/db_store/marker'
6
+ require 'wherever/db_store/store'
7
+ require 'wherever/db_store/dataset'
8
+ require 'wherever/db_store/identifier'
9
+
10
+ require 'wherever/db_store/lookup'
@@ -0,0 +1,30 @@
1
+ class Wherever
2
+ module Accessors
3
+ def get_key_store(*keys)
4
+ mark = keys.last.is_a?(Hash) ? keys.pop["marker"] : marker
5
+ collection(mark).stores.find_or_create_by(:key => keys)
6
+ end
7
+
8
+ def collection(mark=marker)
9
+ DbStore::Marker.find_by_name(mark)
10
+ end
11
+
12
+ def identifier_set
13
+ get_key_store("identifier")
14
+ end
15
+
16
+ def unique_set
17
+ get_key_store("unique")
18
+ end
19
+
20
+ def identifier_key
21
+ key = version_key
22
+ key.delete("version")
23
+ key
24
+ end
25
+
26
+ def version_key
27
+ {}.merge(@options["unique"]).merge(@options["keys"])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,71 @@
1
+ class Wherever
2
+ module Adder
3
+ def add(values, options)
4
+ @options = options
5
+ if record = find
6
+ update(record.diff(values), record)
7
+ else
8
+ create(values)
9
+ end
10
+ end
11
+
12
+ def find
13
+ identifier_set.datasets.where(identifier_key).first
14
+ end
15
+
16
+ def create(values)
17
+ for_unique(values)
18
+ id_record = create_for_identifier(values)
19
+ config.key_groups.each do |group|
20
+ for_group(group, values, id_record)
21
+ end
22
+ end
23
+
24
+ def update(diff, record)
25
+ for_unique(diff)
26
+ id_record = update_for_identifier(diff, record)
27
+ config.key_groups.each do |group|
28
+ for_group(group, diff, id_record)
29
+ end
30
+ end
31
+
32
+ def create_for_identifier(values)
33
+ record = identifier_set.datasets.create(version_key)
34
+ update_record(record, values, record)
35
+ end
36
+
37
+ def update_for_identifier(diff, record)
38
+ record["version"] = @options["unique"]["version"]
39
+ update_record(record, diff, record)
40
+ end
41
+
42
+ def update_record(record, data, id_record, keys=config.keys)
43
+ @grouping.call(record.values, data.clone, id_record, keys)
44
+ record.save!
45
+ record
46
+ end
47
+
48
+ def for_unique(values)
49
+ unique_set.datasets.create(version_key.merge("values" => values.clone))
50
+ end
51
+
52
+ def for_group(group_keys, values, id_record)
53
+ key = {}
54
+ group_keys.each do |key_values|
55
+ k_id = "#{key_values}_id"
56
+ key.merge!(k_id => @options["keys"][k_id])
57
+ end
58
+ store = get_key_store(*group_keys)
59
+ record = store.datasets.find_or_create_by(key)
60
+
61
+ update_record(record, values, id_record, group_keys)
62
+ update_identifier(store, @options["unique"])
63
+ end
64
+
65
+ def update_identifier(store, unique)
66
+ record = store.identifiers.find_or_create_by(config._id => unique[config._id])
67
+ record["version"] = unique["version"]
68
+ record.save
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,31 @@
1
+ class Wherever
2
+ module Getter
3
+ def get(selector, mark=marker)
4
+ keys = get_keys_from(selector) + [{"marker" => mark}]
5
+ result = Hash.new(0)
6
+ if keys.first == "identifier"
7
+ get_key_store(*keys).datasets.where(selector).all.each do |record|
8
+ @grouping.call(result, record.values, result, config)
9
+ end
10
+ result
11
+ else
12
+ get_key_store(*keys).datasets.where(selector).first.try(:values) || {}
13
+ end
14
+ end
15
+
16
+ protected
17
+ def get_keys_from(selector)
18
+ check_keys(selector.keys)
19
+ keys = selector.keys.map{|key| key.gsub(/_id$/, '') }
20
+ config.key_groups.each do |group|
21
+ return group if (group & keys) == group && (keys & group) == keys
22
+ end
23
+ ["identifier"]
24
+ end
25
+
26
+ def check_keys(keys)
27
+ invalid_keys = (keys - config.keys)
28
+ raise InvalidSelector,"Unknown Selector: #{invalid_keys.join(' ')}" unless invalid_keys.empty?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ class Wherever
2
+ module Lookup
3
+ def create_lookup(name, keys)
4
+ lookup = DbStore::Lookup.find_or_create_by(:name => name)
5
+ lookup.keys = keys
6
+ lookup.save
7
+
8
+ self.class.class_eval do
9
+ define_method "set_#{name}" do |version, values|
10
+ lookup, record = create_lookup_record(name, version)
11
+ record.values = key_to_string(values)
12
+ record.save
13
+ recalculate if set_price_lookup(version)
14
+ end
15
+ end
16
+
17
+ self.class.class_eval do
18
+ define_method "get_#{name}" do |version, data|
19
+ lookup, record = get_lookup_record(name, version)
20
+ value_key = lookup.keys.map{|key| data[key]}.join('_')
21
+ record.values[value_key] || 0
22
+ end
23
+ end
24
+ end
25
+
26
+ protected
27
+ def key_to_string(values)
28
+ string_values = {}
29
+ values.each {|k, v| string_values[Array[k].join('_')] = v}
30
+ string_values
31
+ end
32
+
33
+ def set_price_lookup(version)
34
+ return false if collection.price == version
35
+ collection.update_attributes(:price => version)
36
+ end
37
+
38
+ def recalculate
39
+ config.key_groups.each do |group|
40
+ get_key_store(*group).datasets.delete_all
41
+ end
42
+
43
+ identifier_set.datasets.all.each do |record|
44
+ @grouping.call(record.values, nil, record, config.keys)
45
+ record.save!
46
+ config.key_groups.each do |group|
47
+ for_group(group, record.values, record)
48
+ end
49
+ end
50
+ end
51
+
52
+ def get_lookup_record(name, version)
53
+ lookup = DbStore::Lookup.where(:name => name).first
54
+ raise InvalidLookup, "Attempt to access invalid lookup: #{name}" unless lookup
55
+ return [lookup, lookup.versions.find_or_create_by(:name => version)]
56
+ end
57
+
58
+ def create_lookup_record(name, version)
59
+ lookup = DbStore::Lookup.where(:name => name).first
60
+ raise InvalidLookup, "Attempt to access invalid lookup: #{name}" unless lookup
61
+ records = lookup.versions.where(:name => version)
62
+ raise InvalidLookupSetter, "Lookup '#{version}' for '#{name}' already set" unless records.empty?
63
+ return [lookup, lookup.versions.find_or_create_by(:name => version)]
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ class Wherever
2
+ module Mark
3
+ def mark(name)
4
+ marker = collection(name)
5
+ marker.price = collection.price
6
+ collection.stores.all.each do |store|
7
+ marker.stores.create(:key => store.key, :datasets => store.datasets.clone) unless store.key == ["unique"]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ class InvalidSelector < StandardError; end
2
+ class InvalidLookup < StandardError; end
3
+ class InvalidLookupSetter < StandardError; end
4
+
5
+ class Wherever
6
+ include Accessors
7
+ include Adder
8
+ include Getter
9
+ include Lookup
10
+ include Mark
11
+ attr_reader :config, :marker
12
+
13
+ def initialize(options={}, &grouping)
14
+ @config = Configure.new(options)
15
+ @marker = options[:marker] || 'current'
16
+ if block_given?
17
+ @grouping = grouping
18
+ else
19
+ @grouping = lambda do |values, data, record, keys|
20
+ data.keys.each do |key|
21
+ values[key] += data[key]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/wherever.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'mongoid'
2
+ require 'wherever/wherever/accessors'
3
+ require 'wherever/wherever/adder'
4
+ require 'wherever/wherever/getter'
5
+ require 'wherever/wherever/lookup'
6
+ require 'wherever/wherever/mark'
7
+ require 'wherever/wherever'
8
+ require 'wherever/configure'
9
+ require 'wherever/wherever/adder'
10
+ require 'wherever/db_store'
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'wherever'
5
+ require 'ruby-debug'
6
+ require 'database_cleaner'
7
+
8
+ Wherever.new("keys" => ["fund_id"], "database" => 'wherever_test')
9
+
10
+ # Requires supporting files with custom matchers and macros, etc,
11
+ # in ./support/ and its subdirectories.
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
+
14
+ RSpec.configure do |config|
15
+ # config.treat_symbols_as_metadata_keys_with_true_values = true
16
+ config.before(:each) do
17
+ DatabaseCleaner.orm = "mongoid"
18
+ DatabaseCleaner.strategy = :truncation
19
+ DatabaseCleaner.clean
20
+ end
21
+ end
@@ -0,0 +1,121 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Wherever" do
4
+ let(:wherever) { Wherever.new("keys" => keys, "database" => 'wherever_test', "key_groups" => key_groups, "key" => "trade_id") }
5
+ let(:keys) { ["fund_id"] }
6
+ let(:key_groups) { nil }
7
+
8
+ context 'adding the first record' do
9
+ let(:options) { {"unique" => {"trade_id" => 12, "version" => 1}, "keys" => {"fund_id" => 2}} }
10
+ context 'the unique dataset' do
11
+ it 'inserts a record' do
12
+ wherever.add({"settled" => 100, "unsettled" => 0}, options)
13
+ wherever.get_key_store("unique").datasets.should ==
14
+ [DbStore::Dataset.new("trade_id" => 12, "values" => {"unsettled" => 0, "settled" => 100}, "version" => 1, "fund_id" => 2)]
15
+ end
16
+ end
17
+
18
+ context 'the id dataset' do
19
+ it 'inserts a record' do
20
+ wherever.add({"settled" => 100, "unsettled" => 0}, options)
21
+ wherever.get_key_store("identifier").datasets.should ==
22
+ [DbStore::Dataset.new("trade_id" => 12, "values" => {"unsettled" => 0, "settled" => 100}, "version" => 1, "fund_id" => 2)]
23
+ end
24
+ end
25
+
26
+ context 'the key datasets' do
27
+ context 'with a single key' do
28
+ it 'inserts a record' do
29
+ wherever.add({"settled" => 100, "unsettled" => 0}, options)
30
+ wherever.get_key_store("fund").datasets.should ==
31
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 100}, "fund_id" => 2)]
32
+ wherever.get_key_store("fund").identifiers.should ==
33
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
34
+ end
35
+ end
36
+
37
+ context 'with a multiple keys' do
38
+ let(:keys) { ["fund_id", "security_id"] }
39
+ let(:options) { {"unique" => {"trade_id" => 12, "version" => 1}, "keys" => {"fund_id" => 2, "security_id" => 4}} }
40
+
41
+ it 'inserts a record for each key combination' do
42
+ wherever.add({"settled" => 100, "unsettled" => 0}, options)
43
+
44
+ wherever.get_key_store("fund").datasets.should ==
45
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 100}, "fund_id" => 2)]
46
+ wherever.get_key_store("fund").identifiers.should ==
47
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
48
+
49
+ wherever.get_key_store("security").datasets.should ==
50
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 100}, "security_id" => 4)]
51
+ wherever.get_key_store("security").identifiers.should ==
52
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
53
+ end
54
+ end
55
+
56
+ context 'with a multiple keys and grouping configured' do
57
+ let(:keys) { ["fund_id", "security_id"] }
58
+ let(:key_groups) { ["fund", "security", ["fund", "security"]] }
59
+ let(:options) { {"unique" => {"trade_id" => 12, "version" => 1}, "keys" => {"fund_id" => 2, "security_id" => 4}} }
60
+
61
+ it 'inserts a record for each key combination' do
62
+ wherever.add({"settled" => 100, "unsettled" => 0}, options)
63
+
64
+ wherever.get_key_store("fund").datasets.should ==
65
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 100}, "fund_id" => 2)]
66
+ wherever.get_key_store("fund").identifiers.should ==
67
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
68
+
69
+ wherever.get_key_store("security").datasets.should ==
70
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 100}, "security_id" => 4)]
71
+ wherever.get_key_store("security").identifiers.should ==
72
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
73
+
74
+ wherever.get_key_store("fund", "security").datasets.should ==
75
+ [DbStore::Dataset.new("fund_id" => 2, "security_id" => 4, "values" => {"unsettled" => 0, "settled" => 100})]
76
+ wherever.get_key_store("fund", "security").identifiers.should ==
77
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 1)]
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'adding subsequent records' do
84
+ let(:options_first) { {"unique" => {"trade_id" => 12, "version" => 1}, "keys" => {"fund_id" => 2}} }
85
+ let(:options_second) { {"unique" => {"trade_id" => 12, "version" => 2}, "keys" => {"fund_id" => 2}} }
86
+ before do
87
+ wherever.add({"settled" => 100, "unsettled" => 0}, options_first)
88
+ end
89
+
90
+ context 'the unique dataset' do
91
+ it 'add the change' do
92
+ wherever.add({"settled" => 110, "unsettled" => 0}, options_second)
93
+
94
+ wherever.get_key_store("unique").datasets.should ==
95
+ [DbStore::Dataset.new("fund_id" => 2, "trade_id" => 12, "version" => 1, "values" => {"unsettled" => 0, "settled" => 100}),
96
+ DbStore::Dataset.new("fund_id" => 2, "trade_id" => 12, "version" => 2, "values" => {"unsettled" => 0, "settled" => 10})]
97
+ end
98
+ end
99
+
100
+ context 'the identifier dataset' do
101
+ it 'updates the record' do
102
+ wherever.add({"settled" => 110, "unsettled" => 0}, options_second)
103
+
104
+ wherever.get_key_store("identifier").datasets.should ==
105
+ [DbStore::Dataset.new("fund_id" => 2, "trade_id" => 12, "version" => 2, "values" => {"unsettled" => 0, "settled" => 110})]
106
+ end
107
+ end
108
+
109
+ context 'the key datasets' do
110
+ context 'with a single key' do
111
+ it 'inserts a record' do
112
+ wherever.add({"settled" => 110, "unsettled" => 0}, options_second)
113
+ wherever.get_key_store("fund").datasets.should ==
114
+ [DbStore::Dataset.new("values" => {"unsettled" => 0, "settled" => 110}, "fund_id" => 2)]
115
+ wherever.get_key_store("fund").identifiers.should ==
116
+ [DbStore::Identifier.new("trade_id" => 12, "version" => 2)]
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end