light_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in light_store.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael Kompanets
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # LightStore
2
+
3
+ A library for storing data about an object in spreadsheet-like format (array of hashes).
4
+
5
+ This library aims to provide an easy way to store data about objects. Data that is typically generated with complex queries could be stored in a flat format. This is meant to be a general purpose library, but it was created to improve performance of dynamic report generators. This comes from an idea that each 'row' of report data could be identified by the object id and a secondary id relevant to the report.
6
+
7
+ Data format example: `[{account_id: 1, month: '2014-11', revenue: 987.65}, {account_id: 1, month: '2014-12', revenue: 1234.56}]`
8
+
9
+ In the above example `account_id:` is a primary id and `month:` is a secondary id.
10
+
11
+ ## Installation
12
+
13
+ This gem relies on redis for storage.
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'light_store'
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ ### In rails
24
+
25
+ Add an initializer:
26
+
27
+ ```ruby
28
+ LightStore.configure do |config|
29
+ config.redis = Redis.new
30
+ end
31
+ ```
32
+
33
+ Define a class:
34
+
35
+ ```ruby
36
+ class RevenueReport < LightStore::Data
37
+ set_prefix 'revenue_report'
38
+ set_primary_key :id # default
39
+ set_secondary_key :month # required
40
+
41
+ set_sortable_field :month, :datetime
42
+ set_sortable_field :revenue, :float
43
+ set_sortable_field :number_of_orders, :integer
44
+ end
45
+ ```
46
+
47
+ Adding data:
48
+
49
+ ```ruby
50
+ data = [
51
+ {id: 1, month: '2012-01', revenue: 1234.56, number_of_orders: 10},
52
+ {id: 1, month: '2012-02', revenue: 2345.56, number_of_orders: 20},
53
+ {id: 1, month: '2012-03', revenue: 3245.56, number_of_orders: 30},
54
+ {id: 1, month: '2012-04', revenue: 2435.56, number_of_orders: 20},
55
+ {id: 2, month: '2012-01', revenue: 1234.56, number_of_orders: 10},
56
+ {id: 2, month: '2012-02', revenue: 2234.56, number_of_orders: 20},
57
+ {id: 2, month: '2012-03', revenue: 3234.56, number_of_orders: 30},
58
+ {id: 2, month: '2012-04', revenue: 2234.56, number_of_orders: 20},
59
+ ]
60
+ RevenueReport.add_data(data)
61
+ ```
62
+
63
+ Getting all data:
64
+ ```ruby
65
+ data = RevenueReport.get_data()
66
+ ```
67
+
68
+ Getting data for a given id:
69
+ ```ruby
70
+ data = RevenueReport.get_data(2)
71
+ ```
72
+
73
+ Getting data for a given id within range:
74
+ ```ruby
75
+ data = RevenueReport.get_sorted_data(2, :month, '2012-02', '2012-04')
76
+ # or
77
+ data = RevenueReport.get_sorted_data(2, :number_of_orders, 20, 30)
78
+ ```
79
+
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ require "light_store/version"
2
+ require "light_store/configuration"
3
+ require "light_store/class_methods"
4
+
5
+ module LightStore
6
+
7
+ def self.configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ def self.configure
12
+ yield(configuration) if block_given?
13
+ end
14
+
15
+ class << self
16
+ def included(base)
17
+ base.extend ClassMethods
18
+ end
19
+ end
20
+
21
+ class Data
22
+ include LightStore
23
+
24
+ def namespace
25
+ self.class.namespace
26
+ end
27
+
28
+ def primary_key
29
+ self.class.primary_key
30
+ end
31
+
32
+ def secondary_key
33
+ self.class.secondary_key
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,205 @@
1
+ module LightStore
2
+ module ClassMethods
3
+ def datastore
4
+ LightStore.configuration.redis
5
+ end
6
+
7
+ def set_namespace(x)
8
+ @namespace = x
9
+ end
10
+
11
+ def namespace
12
+ @namespace ||= 'light_store'
13
+ end
14
+
15
+ def set_prefix(x)
16
+ @prefix = x
17
+ end
18
+
19
+ def prefix
20
+ @prefix ||= self.name
21
+ end
22
+
23
+ def set_primary_key(x)
24
+ @primary_key = x
25
+ end
26
+
27
+ def primary_key
28
+ @primary_key ||= :id
29
+ end
30
+
31
+ def set_secondary_key(x)
32
+ @secondary_key = x
33
+ end
34
+
35
+ def secondary_key
36
+ raise ArgumentError, 'secondary_key must be set' unless @secondary_key
37
+ @secondary_key
38
+ end
39
+
40
+ def set_sortable_field(field_name, field_type = :integer)
41
+ allowed_field_types = [:integer, :float, :datetime]
42
+ raise ArgumentError, 'field_type must be [:integer, :float, :datetime]' unless allowed_field_types.include?(field_type)
43
+ h = {field_name: field_name, field_type: field_type}
44
+ @sortable_fields = self.sortable_fields
45
+ @sortable_fields.push(h) unless @sortable_fields.include?(h)
46
+ end
47
+
48
+ def sortable_fields
49
+ @sortable_fields ||= []
50
+ end
51
+
52
+ def marshal(h)
53
+ Marshal.dump(h)
54
+ end
55
+
56
+ def unmarshal(h)
57
+ Marshal.load(h)
58
+ end
59
+
60
+ def base_key
61
+ "#{namespace}:#{prefix}"
62
+ end
63
+
64
+ def base_data_key
65
+ "#{base_key}:data"
66
+ end
67
+
68
+ def base_member_ids_key
69
+ "#{base_key}:member_ids"
70
+ end
71
+
72
+ def base_sorted_member_ids_key
73
+ "#{base_key}:sorted_member_ids"
74
+ end
75
+
76
+ def get_primary_id(h)
77
+ raise ArgumentError, 'primary_id must be set' unless h[primary_key]
78
+ h[primary_key]
79
+ end
80
+
81
+ def get_secondary_id(h)
82
+ raise ArgumentError, 'secondary_key must be set' unless h[secondary_key]
83
+ h[secondary_key]
84
+ end
85
+
86
+ def get_data_row_key(h)
87
+ "#{base_data_key}:#{get_primary_id(h)}:#{get_secondary_id(h)}"
88
+ end
89
+
90
+ def get_member_id_key(h)
91
+ "#{base_member_ids_key}:#{get_primary_id(h)}"
92
+ end
93
+
94
+ def get_sorted_member_id_key(_primary_id, _sortable_field)
95
+ "#{base_sorted_member_ids_key}:#{_primary_id}:#{_sortable_field}"
96
+ end
97
+
98
+ def get_score(value, sortable_field_type)
99
+ raise ArgumentError, "value for #{sortable_field_type} must be set" unless value
100
+ case sortable_field_type
101
+ when :integer
102
+ value.to_i
103
+ when :float
104
+ value.to_f
105
+ when :datetime
106
+ if value.is_a? Time
107
+ value.to_i
108
+ else
109
+ Time.parse(value).to_i
110
+ end
111
+ else
112
+ raise ArgumentError, "score value for #{sortable_field_type} must be in proper format"
113
+ end
114
+ end
115
+
116
+ def add_member_id(h)
117
+ member_id_key = get_member_id_key(h)
118
+ data_row_key = get_data_row_key(h)
119
+ datastore.sadd(member_id_key, data_row_key)
120
+ end
121
+
122
+ def add_sorted_member_ids(h)
123
+ sortable_fields.each do |s|
124
+ field_name = s[:field_name]
125
+ field_type = s[:field_type]
126
+ add_sorted_member_id(h, field_name, field_type)
127
+ end
128
+ end
129
+
130
+ def add_sorted_member_id(h, field_name, field_type)
131
+ primary_id = get_primary_id(h)
132
+ sorted_member_id = get_sorted_member_id_key(primary_id, field_name)
133
+ data_row_key = get_data_row_key(h)
134
+ value = h[field_name]
135
+ score = get_score(value, field_type)
136
+ datastore.zadd(sorted_member_id, score, data_row_key)
137
+ end
138
+
139
+ def get_member_ids(_primary_key = nil)
140
+ key = _primary_key ? "#{base_member_ids_key}:#{_primary_key}" : base_member_ids_key
141
+ member_keys = datastore.keys("#{key}*")
142
+ member_keys.collect{ |k| datastore.smembers(k) }.flatten
143
+ end
144
+
145
+ def add_data(data)
146
+ added_records_count = 0
147
+ data.each do |h|
148
+ data_row_key = get_data_row_key(h)
149
+ marshaled_h = marshal(h)
150
+ new_member_check = add_member_id(h)
151
+ if new_member_check
152
+ datastore.set(data_row_key, marshaled_h)
153
+ add_sorted_member_ids(h)
154
+ else
155
+ old_value = datastore.getset(data_row_key, marshaled_h)
156
+ if old_value != marshaled_h
157
+ add_sorted_member_ids(h)
158
+ end
159
+ end
160
+ added_records_count += 1 if new_member_check
161
+ end
162
+ #plural_records = added_records_count == 1 ? "record" : "records"
163
+ return added_records_count
164
+ end
165
+
166
+ def get_data(_primary_key = nil)
167
+ data_keys = _primary_key ? get_member_ids(_primary_key) : get_member_ids()
168
+ return [] if data_keys.empty?
169
+ marshaled_data = datastore.mget(data_keys)
170
+ unmarshaled_data = marshaled_data.collect{ |d| unmarshal(d) }
171
+ end
172
+
173
+ def get_sorted_data(_primary_key, _sortable_field, min, max, options = {})
174
+ grouped_sortable_fields = sortable_fields.group_by { |f| f[:field_name] }
175
+ raise ArgumentError, "No sortable fields declared" if grouped_sortable_fields.empty?
176
+ raise ArgumentError, "sortable field '#{_sortable_field}' not declared" unless grouped_sortable_fields.has_key?(_sortable_field)
177
+ field_type = grouped_sortable_fields[_sortable_field].first[:field_type]
178
+ min = get_score(min, field_type) unless min == '-inf'
179
+ max = get_score(max, field_type) unless max == '+inf'
180
+ sorted_member_id = get_sorted_member_id_key(_primary_key, _sortable_field)
181
+
182
+ data_keys = datastore.zrangebyscore(sorted_member_id, min, max, options)
183
+ marshaled_data = datastore.mget(data_keys)
184
+ unmarshaled_data = marshaled_data.collect{ |d| unmarshal(d) }
185
+ end
186
+
187
+ def clear_all_data
188
+ data_keys = datastore.keys("#{base_member_ids_key}*")
189
+ data_keys = data_keys.concat(datastore.keys("#{base_sorted_member_ids_key}*"))
190
+ data_keys = data_keys.concat(datastore.keys("#{base_data_key}*"))
191
+ clear(data_keys)
192
+ end
193
+
194
+ def clear_all
195
+ all_keys = datastore.keys("#{base_key}*")
196
+ clear(all_keys)
197
+ end
198
+
199
+ # make private.
200
+ def clear(keys)
201
+ deleted_count = datastore.del(keys) unless keys.empty?
202
+ end
203
+
204
+ end
205
+ end
@@ -0,0 +1,15 @@
1
+ module LightStore
2
+ class Configuration
3
+ def initialize
4
+ self.redis = nil
5
+ end
6
+
7
+ def redis
8
+ @redis
9
+ end
10
+
11
+ def redis=(r)
12
+ @redis = r
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module LightStore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'light_store/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "light_store"
8
+ spec.version = LightStore::VERSION
9
+ spec.authors = ["Michael Kompanets"]
10
+ spec.email = ["mkompanets@gmail.com"]
11
+ spec.description = %q{A library for storing data about an object in spreadsheet-like format (array of hashes).}
12
+ spec.summary = %q{This library aims to provide an easy way to store report data about objects. Data that is typically generated with complex queries and methods could be stored in a flat and accessible format. This is meant to be a general purpose library, but it was created to improve performance of dynamic report generators. This comes from an idea that each 'row' of report data could be identified by the object id and a secondary id relevant to the report.}
13
+ spec.homepage = "https://github.com/mkompanets/light_store"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "redis"
26
+ end
@@ -0,0 +1,299 @@
1
+ require 'light_store'
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ Bundler.setup(:default, :test)
5
+ Bundler.require(:default, :test)
6
+
7
+ require 'rspec'
8
+ require 'redis'
9
+ require 'logger'
10
+ describe LightStore::Data do
11
+ before(:each) do
12
+ LightStore.configure do |config|
13
+ config.redis = Redis.new
14
+ end
15
+ end
16
+ context 'for class initialization' do
17
+ before(:each) do
18
+ class DefaultValuesClass < LightStore::Data
19
+ end
20
+ class SetValuesClass < LightStore::Data
21
+ set_namespace 'some_namespace'
22
+ set_prefix 'some_prefix'
23
+ set_primary_key 'some_id'
24
+ set_secondary_key 'some_secondary_key'
25
+ set_sortable_field :f1
26
+ set_sortable_field :f2, :integer
27
+ set_sortable_field :f3, :float
28
+ set_sortable_field :f4, :datetime
29
+ end
30
+ SetValuesClass.clear_all
31
+ end
32
+ describe 'DefaultValuesClass' do
33
+ let(:default_values_instance) { DefaultValuesClass.new }
34
+ it 'datastore type is Redis' do
35
+ DefaultValuesClass.datastore.class.name.should == 'Redis'
36
+ end
37
+ it 'sets class level namespace to light_store' do
38
+ DefaultValuesClass.namespace.should == 'light_store'
39
+ end
40
+ it 'sets class level prefix to class name' do
41
+ DefaultValuesClass.prefix.should == 'DefaultValuesClass'
42
+ end
43
+ it 'sets instance level namespace to light_store' do
44
+ default_values_instance.namespace.should == 'light_store'
45
+ end
46
+ it 'sets class level primary_key to :id by default' do
47
+ DefaultValuesClass.primary_key.should == :id
48
+ end
49
+ it 'sets instance level primary_key to :id by default' do
50
+ default_values_instance.primary_key.should == :id
51
+ end
52
+ it 'raises ArgumentError if secondary_key not set' do
53
+ expect{DefaultValuesClass.secondary_key}.to raise_error(ArgumentError)
54
+ end
55
+ end
56
+ describe 'SetValuesClass' do
57
+ let(:set_values_instance) { SetValuesClass.new }
58
+ let(:sample_h1) {
59
+ {
60
+ 'some_id' => 1,
61
+ 'some_secondary_key' => 's2',
62
+ f1: '1',
63
+ f2: '2',
64
+ f3: '3.2',
65
+ f4: Time.now,
66
+ }
67
+ }
68
+ context 'for class methods' do
69
+ it 'sets class namespace to some_namespace' do
70
+ SetValuesClass.namespace.should == 'some_namespace'
71
+ end
72
+ it 'sets class level prefix to some_prefix' do
73
+ SetValuesClass.prefix.should == 'some_prefix'
74
+ end
75
+ it 'sets class primary_key to some_id' do
76
+ SetValuesClass.primary_key.should == 'some_id'
77
+ end
78
+ it 'sets class secondary_key to some_secondary_key' do
79
+ SetValuesClass.secondary_key.should == 'some_secondary_key'
80
+ end
81
+ end
82
+ context 'for key setting methods' do
83
+ describe '#base_key' do
84
+ it 'sets key to #{namespace}:#{prefix}' do
85
+ k = "#{SetValuesClass.namespace}:#{SetValuesClass.prefix}"
86
+ SetValuesClass.base_key.should == k
87
+ end
88
+ end
89
+ describe '#base_member_ids_key' do
90
+ it 'sets key to #{base_key}:member_ids' do
91
+ k = "#{SetValuesClass.base_key}:member_ids"
92
+ SetValuesClass.base_member_ids_key.should == k
93
+ end
94
+ end
95
+ describe '#base_sorted_member_ids_key' do
96
+ it 'sets key to "#{base_key}:sorted_member_ids"' do
97
+ k = "#{SetValuesClass.base_key}:sorted_member_ids"
98
+ SetValuesClass.base_sorted_member_ids_key.should == k
99
+ end
100
+ end
101
+ describe '#base_data_key' do
102
+ it 'sets key to #{namespace}:#{prefix}:data' do
103
+ k = "#{SetValuesClass.namespace}:#{SetValuesClass.prefix}:data"
104
+ SetValuesClass.base_data_key.should == k
105
+ end
106
+ end
107
+ describe '#get_primary_id' do
108
+ it 'gets primary key' do
109
+ k = sample_h1['some_id']
110
+ SetValuesClass.get_primary_id(sample_h1).should == k
111
+ end
112
+ it 'raises ArgumentError if not set' do
113
+ expect{SetValuesClass.get_primary_id({})}.to raise_error(ArgumentError)
114
+ end
115
+ end
116
+ describe '#get_secondary_id' do
117
+ it 'gets secondary_key key' do
118
+ k = sample_h1['some_secondary_key']
119
+ SetValuesClass.get_secondary_id(sample_h1).should == k
120
+ end
121
+ it 'raises ArgumentError if not set' do
122
+ expect{SetValuesClass.get_secondary_id({})}.to raise_error(ArgumentError)
123
+ end
124
+ end
125
+ describe '#sortable_fields' do
126
+ it 'field_type defaults to integer' do
127
+ SetValuesClass.sortable_fields.should include({field_name: :f1, field_type: :integer})
128
+ end
129
+ end
130
+ describe '#get_data_row_key' do
131
+ it 'gets data row key' do
132
+ d = SetValuesClass.get_data_row_key(sample_h1)
133
+ base_key = SetValuesClass.base_data_key
134
+ primary_id = SetValuesClass.get_primary_id(sample_h1)
135
+ secondary_id = SetValuesClass.get_secondary_id(sample_h1)
136
+ d.should == "#{base_key}:#{primary_id}:#{secondary_id}"
137
+ end
138
+ end
139
+ describe '#get_member_id_key' do
140
+ it 'gets member id key' do
141
+ d = SetValuesClass.get_member_id_key(sample_h1)
142
+ base_key = SetValuesClass.base_member_ids_key
143
+ primary_id = SetValuesClass.get_primary_id(sample_h1)
144
+ d.should == "#{base_key}:#{primary_id}"
145
+ end
146
+ end
147
+ describe '#get_sorted_member_id_key' do
148
+ it 'gets sorted member id key' do
149
+ field_name = SetValuesClass.sortable_fields.first[:field_name]
150
+ primary_id = SetValuesClass.get_primary_id(sample_h1)
151
+ d = SetValuesClass.get_sorted_member_id_key(sample_h1, field_name)
152
+ end
153
+ end
154
+ describe '#get_score' do
155
+ it 'gets the score for integer type' do
156
+ v = 1
157
+ SetValuesClass.get_score(v, :integer).should be_a Integer
158
+ end
159
+ it 'gets the score for float type' do
160
+ v = 1
161
+ SetValuesClass.get_score(v, :float).should be_a Float
162
+ end
163
+ it 'gets the score for datetime type' do
164
+ v = Time.now.to_s
165
+ SetValuesClass.get_score(v, :datetime).should be_a Integer
166
+ end
167
+ it 'raises an error for nil value' do
168
+ v = nil
169
+ expect{SetValuesClass.get_score(v, :unknown)}.to raise_error(ArgumentError)
170
+ end
171
+ it 'raises an error for unexpected type' do
172
+ v = Time.now.to_s
173
+ expect{SetValuesClass.get_score(v, :unknown)}.to raise_error(ArgumentError)
174
+ end
175
+ end
176
+ describe '#add_member_id' do
177
+ it 'adds member id to set' do
178
+ SetValuesClass.add_member_id(sample_h1).should be_true
179
+ row_id = SetValuesClass.get_data_row_key(sample_h1)
180
+ SetValuesClass.get_member_ids.should include(row_id)
181
+ end
182
+ end
183
+ describe '#add_sorted_member_ids' do
184
+ it 'adds sorted member ids to set' do
185
+ SetValuesClass.add_sorted_member_ids(sample_h1)
186
+ end
187
+ end
188
+ describe '#add_sorted_member_id' do
189
+ it 'adds sorted member id to set' do
190
+ field_name = SetValuesClass.sortable_fields.first[:field_name]
191
+ field_type = SetValuesClass.sortable_fields.first[:field_type]
192
+ SetValuesClass.add_sorted_member_id(sample_h1, field_name, field_type)
193
+ end
194
+ end
195
+ end
196
+ context 'for instance methods' do
197
+ it 'sets instance namespace to some_namespace' do
198
+ set_values_instance.namespace.should == 'some_namespace'
199
+ end
200
+ it 'sets instance primary_key to some_id' do
201
+ set_values_instance.primary_key.should == 'some_id'
202
+ end
203
+ it 'sets instance secondary_key to some_secondary_key' do
204
+ set_values_instance.secondary_key.should == 'some_secondary_key'
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ context 'for class methods' do
211
+ let(:dataset1) {
212
+ [
213
+ {id: 2, s_id: 20, name_1: 'John', name_2: 'Krakis', super_date: Time.new(2002)},
214
+ {id: 2, s_id: 25, name_1: 'Jake', name_2: 'Harken', super_date: Time.new(2003)},
215
+ {id: 3, s_id: 30, name_1: 'Jake', name_2: 'Marken', super_date: Time.new(2004)},
216
+ {id: 3, s_id: 35, name_1: 'Jake', name_2: 'Barken', super_date: Time.new(2005)},
217
+ {id: 3, s_id: 40, name_1: 'Jake', name_2: 'Farken', super_date: Time.new(2006)},
218
+ {id: 3, s_id: 45, name_1: 'Jake', name_2: 'Larken', super_date: Time.new(2007)},
219
+ {id: 3, s_id: 50, name_1: 'Jake', name_2: 'Tarken', super_date: Time.new(2008)},
220
+ ]
221
+ }
222
+ before(:each) do
223
+ class SomeData < LightStore::Data
224
+ set_prefix 'some_data'
225
+ set_secondary_key :s_id
226
+ set_sortable_field :super_date, :datetime
227
+ end
228
+ SomeData.clear_all
229
+ end
230
+
231
+ describe '#add_data' do
232
+ it 'sets data' do
233
+ new_rows = SomeData.add_data(dataset1)
234
+ end
235
+ it 'does not duplicate data' do
236
+ SomeData.add_data(dataset1)
237
+ new_rows = SomeData.add_data(dataset1)
238
+ new_rows.should == 0
239
+ end
240
+ end
241
+ describe '#get_data' do
242
+ it 'gets data' do
243
+ new_rows = SomeData.add_data(dataset1)
244
+ data = SomeData.get_data
245
+ data.size.should == new_rows
246
+ end
247
+ it 'gets data in proper format' do
248
+ SomeData.add_data(dataset1)
249
+ data = SomeData.get_data
250
+ data.should be_a Array
251
+ data.first.should be_a Hash
252
+ end
253
+ it 'gets data correctly by id' do
254
+ SomeData.add_data(dataset1)
255
+
256
+ # Two rows for id: 2
257
+ data = SomeData.get_data(2)
258
+ data.size.should == 2
259
+
260
+ # Five rows for id: 3
261
+ data = SomeData.get_data(3)
262
+ data.size.should == 5
263
+ end
264
+ end
265
+ describe '#get_sorted_data' do
266
+ it 'get sorted data' do
267
+ SomeData.add_data(dataset1)
268
+ data = SomeData.get_sorted_data(3, :super_date, Time.new(2003), Time.new(2007))
269
+ data.size.should == 4
270
+ end
271
+ it 'get sorted data includes correctly sorted values' do
272
+ SomeData.add_data(dataset1)
273
+ data = SomeData.get_sorted_data(3, :super_date, Time.new(2003), Time.new(2007))
274
+ values_within_range = true
275
+ data.each do |h|
276
+ sorted_value = h[:super_date]
277
+ values_between_range = false unless sorted_value >= Time.new(2003) && sorted_value <= Time.new(2007)
278
+ end
279
+ values_within_range.should be_true
280
+ end
281
+ end
282
+ describe '#clear_all_data' do
283
+ it 'clears all data' do
284
+ SomeData.add_data(dataset1)
285
+ SomeData.clear_all_data
286
+ data = SomeData.get_data
287
+ data.size.should == 0
288
+ end
289
+ end
290
+ describe '#clear_all' do
291
+ it 'clears all data' do
292
+ SomeData.add_data(dataset1)
293
+ SomeData.clear_all
294
+ all_keys = SomeData.datastore.keys("#{SomeData.base_key}*")
295
+ all_keys.size.should == 0
296
+ end
297
+ end
298
+ end
299
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: light_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Kompanets
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redis
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: A library for storing data about an object in spreadsheet-like format
79
+ (array of hashes).
80
+ email:
81
+ - mkompanets@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - lib/light_store.rb
92
+ - lib/light_store/class_methods.rb
93
+ - lib/light_store/configuration.rb
94
+ - lib/light_store/version.rb
95
+ - light_store.gemspec
96
+ - spec/light_store_spec.rb
97
+ homepage: https://github.com/mkompanets/light_store
98
+ licenses:
99
+ - MIT
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 1.8.25
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: This library aims to provide an easy way to store report data about objects. Data
122
+ that is typically generated with complex queries and methods could be stored in
123
+ a flat and accessible format. This is meant to be a general purpose library, but
124
+ it was created to improve performance of dynamic report generators. This comes
125
+ from an idea that each 'row' of report data could be identified by the object id
126
+ and a secondary id relevant to the report.
127
+ test_files:
128
+ - spec/light_store_spec.rb
129
+ has_rdoc: