light_store 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb309c6ef19cbc223dae997c56c151c239eed2bc
4
+ data.tar.gz: 1602d8998fd1aa3eee4b0dc530f36d8588ee6f42
5
+ SHA512:
6
+ metadata.gz: e203288dcb0e5740f2471801116e19b840fa9b1f75d20aa38920289216611020314de90088be18800dfd2e82897fee1be1b36277db685e7f4bf8d2991f024f97
7
+ data.tar.gz: 4fd957efae0dc08162dbcb8c83da8447f1788bfb80aba877904e27884d64d2f4e62a87cee53e6b24e6b4c7436405cb5defca5d8dd199751a0a00499c096551df
data/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  A library for storing data about an object in spreadsheet-like format (array of hashes).
4
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.
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 date constraint relevant to the report.
6
6
 
7
7
  Data format example: `[{account_id: 1, month: '2014-11', revenue: 987.65}, {account_id: 1, month: '2014-12', revenue: 1234.56}]`
8
8
 
9
- In the above example `account_id:` is a primary id and `month:` is a secondary id.
9
+ In the above example `account_id:` is a primary key and `month:` is a date constraint field.
10
10
 
11
11
  ## Installation
12
12
 
13
- This gem relies on redis for storage.
13
+ This gem relies on [redis](http://redis.io/) for storage. This gem depends on [redis-rb](https://github.com/redis/redis-rb) gem. Please be sure that it's installed and working correctly.
14
14
 
15
15
  Add this line to your application's Gemfile:
16
16
 
@@ -34,13 +34,8 @@ Define a class:
34
34
 
35
35
  ```ruby
36
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
37
+ primary_field :id
38
+ date_constraint_field :month # required
44
39
  end
45
40
  ```
46
41
 
@@ -67,16 +62,16 @@ data = RevenueReport.get_data()
67
62
 
68
63
  Getting data for a given id:
69
64
  ```ruby
70
- data = RevenueReport.get_data(2)
65
+ data = RevenueReport.get_data(primary_key: 2)
71
66
  ```
72
67
 
73
68
  Getting data for a given id within range:
74
69
  ```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)
70
+ data = RevenueReport.get_sorted_data(primary_key: 2, date_range: ['2012-02', '2012-04'])
78
71
  ```
79
72
 
73
+ Running `RevenueReport.add_data(data)` for the same data will not result in duplication. The dataset passed to `light_store` instance could be the same or slightly different, it doesn't matter. All records will be handled correctly. `LightStore` will generate a key that combines primary key and date constraint field. Data corresponding to that record will be either created or updated.
74
+
80
75
 
81
76
  ## Contributing
82
77
 
@@ -1,6 +1,7 @@
1
1
  require "light_store/version"
2
2
  require "light_store/configuration"
3
3
  require "light_store/class_methods"
4
+ require "light_store/class_accessor_methods"
4
5
 
5
6
  module LightStore
6
7
 
@@ -15,6 +16,7 @@ module LightStore
15
16
  class << self
16
17
  def included(base)
17
18
  base.extend ClassMethods
19
+ base.extend ClassAccessorMethods
18
20
  end
19
21
  end
20
22
 
@@ -0,0 +1,51 @@
1
+ module LightStore
2
+ module ClassAccessorMethods
3
+ def base_name
4
+ "LightStore:#{self.name}"
5
+ end
6
+
7
+ def primary_field(x = nil)
8
+ @primary_field = x unless x.nil?
9
+ raise ArgumentError, 'primary_field must be set' unless @primary_field
10
+ @primary_field
11
+ end
12
+
13
+ def date_constraint_field(x = nil)
14
+ @date_constraint_field = x if x
15
+ raise ArgumentError, 'date_constraint_field must be set' unless @date_constraint_field
16
+ @date_constraint_field
17
+ end
18
+
19
+ def row=(h)
20
+ @row = h
21
+ end
22
+
23
+ def row
24
+ @row
25
+ end
26
+
27
+ def primary_key
28
+ row[primary_field]
29
+ end
30
+
31
+ def secondary_key
32
+ Time.parse(row[date_constraint_field].to_s).to_i
33
+ end
34
+
35
+ def row_key
36
+ "#{base_name}:row:#{primary_key}:#{secondary_key}"
37
+ end
38
+
39
+ def row_reference_key
40
+ "#{base_name}:key:#{primary_key}:#{secondary_key}"
41
+ end
42
+
43
+ def primary_range_key(x)
44
+ "#{base_name}:primary_range:#{x}"
45
+ end
46
+
47
+ def range_key
48
+ "#{base_name}:range"
49
+ end
50
+ end
51
+ end
@@ -4,51 +4,6 @@ module LightStore
4
4
  LightStore.configuration.redis
5
5
  end
6
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
7
  def marshal(h)
53
8
  Marshal.dump(h)
54
9
  end
@@ -57,149 +12,97 @@ module LightStore
57
12
  Marshal.load(h)
58
13
  end
59
14
 
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
15
+ def time_to_integer(t)
16
+ case t
17
+ when Integer, String
18
+ t = Time.new(t)
19
+ when Date, Time
20
+ # do nothing
111
21
  else
112
- raise ArgumentError, "score value for #{sortable_field_type} must be in proper format"
22
+ raise ArgumentError, "#{t.inspect}: #{t.class.name} should be string,integer,date or time"
113
23
  end
24
+ Time.parse(t.to_s).to_i
114
25
  end
115
26
 
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)
27
+ def add_data(data)
28
+ add_data_block = proc {
29
+ data.each do |h|
30
+ self.row = h
31
+ self.persist_row
32
+ end
33
+ }
34
+ if LightStore.configuration.pipelined?
35
+ datastore.pipelined do
36
+ add_data_block.call
37
+ end
38
+ else
39
+ add_data_block.call
127
40
  end
128
41
  end
129
42
 
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)
43
+ def persist_row
44
+ datastore.set(row_key, marshal(self.row))
45
+ datastore.set(row_reference_key, row_key)
46
+ _primary_range_key = primary_range_key(self.primary_key)
47
+ datastore.zadd(_primary_range_key, secondary_key, row_reference_key)
48
+ datastore.zadd(range_key, secondary_key, row_reference_key)
137
49
  end
138
50
 
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)
51
+ def get_data(options = {})
52
+ reference_keys = []
53
+ if options.has_key?(:primary_key)
54
+ # With primary key.
55
+ if options.has_key?(:date_range)
56
+ # With date range.
57
+ reference_keys = get_data_by_range(options[:primary_key], *options[:date_range])
154
58
  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
59
+ # date_range was not passed in, getting data for the key.
60
+ reference_keys = get_all_reference_keys(options[:primary_key])
61
+ end
62
+ else
63
+ # Without primary key.
64
+ if options.has_key?(:date_range)
65
+ reference_keys = get_data_by_range(nil, *options[:date_range])
66
+ else
67
+ # date_range was not passed in, getting all data.
68
+ reference_keys = get_all_reference_keys()
159
69
  end
160
- added_records_count += 1 if new_member_check
161
70
  end
162
- #plural_records = added_records_count == 1 ? "record" : "records"
163
- return added_records_count
71
+ return [] if reference_keys.empty?
72
+ get_data_by_reference_keys(reference_keys)
164
73
  end
165
74
 
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) }
75
+ def get_data_by_reference_keys(reference_keys)
76
+ row_keys = datastore.mget(reference_keys)
77
+ marshaled_data = datastore.mget(row_keys)
78
+ marshaled_data.collect{ |d| unmarshal(d) }
171
79
  end
172
80
 
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) }
81
+ def get_all_reference_keys(primary_key = nil)
82
+ key_substring = "#{base_name}:key:#{primary_key}*"
83
+ datastore.keys(key_substring)
185
84
  end
186
85
 
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)
86
+ def get_data_by_range(_primary_key = nil, start_date, stop_date)
87
+ min = time_to_integer(start_date)
88
+ max = time_to_integer(stop_date)
89
+ if _primary_key.nil?
90
+ # range_key
91
+ return datastore.zrangebyscore(range_key, min, max)
92
+ else
93
+ # primary_range_key
94
+ _primary_range_key = primary_range_key(_primary_key)
95
+ return datastore.zrangebyscore(_primary_range_key, min, max)
96
+ end
192
97
  end
193
98
 
194
99
  def clear_all
195
- all_keys = datastore.keys("#{base_key}*")
100
+ all_keys = datastore.keys("#{base_name}*")
196
101
  clear(all_keys)
197
102
  end
198
103
 
199
- # make private.
200
104
  def clear(keys)
201
105
  deleted_count = datastore.del(keys) unless keys.empty?
202
106
  end
203
-
204
107
  end
205
108
  end
@@ -2,6 +2,7 @@ module LightStore
2
2
  class Configuration
3
3
  def initialize
4
4
  self.redis = nil
5
+ self.pipelined = true
5
6
  end
6
7
 
7
8
  def redis
@@ -11,5 +12,13 @@ module LightStore
11
12
  def redis=(r)
12
13
  @redis = r
13
14
  end
15
+
16
+ def pipelined?
17
+ @pipelined
18
+ end
19
+
20
+ def pipelined=(b)
21
+ @pipelined = b
22
+ end
14
23
  end
15
24
  end
@@ -1,3 +1,3 @@
1
1
  module LightStore
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "faker"
25
+ spec.add_development_dependency "mock_redis"
24
26
 
25
27
  spec.add_dependency "redis"
26
28
  end
@@ -0,0 +1,79 @@
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
+ config.pipelined = false
15
+ end
16
+ end
17
+ context 'for class accessor methods' do
18
+ before(:each) do
19
+ class TestReport < LightStore::Data
20
+ primary_field :some_field_name
21
+ date_constraint_field :some_date_field
22
+ end
23
+ class DefaultTestReport < LightStore::Data
24
+ end
25
+ end
26
+ context 'for base_name' do
27
+ it 'sets base_name' do
28
+ TestReport.base_name.should == 'LightStore:TestReport'
29
+ end
30
+ end
31
+ context 'for primary_field' do
32
+ it 'returns primary_field' do
33
+ TestReport.primary_field.should_not be_nil
34
+ end
35
+ it 'errors without primary_field' do
36
+ expect{DefaultTestReport.primary_field}
37
+ .to raise_error(ArgumentError, 'primary_field must be set')
38
+ end
39
+ end
40
+ context 'for date_constraint_field' do
41
+ it 'returns date_constraint_field' do
42
+ TestReport.date_constraint_field.should_not be_nil
43
+ end
44
+ it 'errors without date_constraint_field' do
45
+ expect{DefaultTestReport.date_constraint_field}
46
+ .to raise_error(ArgumentError, 'date_constraint_field must be set')
47
+ end
48
+ end
49
+ context 'for row' do
50
+ let(:date) { '2012-05-15' }
51
+ let(:key) { 1 }
52
+ let(:h) {{some_field_name: 1, some_date_field: date}}
53
+ before(:each) do
54
+ TestReport.row = h
55
+ end
56
+ it 'sets and gets row' do
57
+ TestReport.row.should == h
58
+ end
59
+ it 'gets primary_key' do
60
+ TestReport.primary_key.should == key
61
+ end
62
+ it 'gets secondary_key' do
63
+ TestReport.secondary_key.should == Time.parse(date).to_i
64
+ end
65
+ it 'has row_key' do
66
+ TestReport.row_key.should match(":row:#{TestReport.primary_key}:#{TestReport.secondary_key}")
67
+ end
68
+ it 'has row_reference_key' do
69
+ TestReport.row_reference_key.should match(":key:#{TestReport.primary_key}:#{TestReport.secondary_key}")
70
+ end
71
+ it 'has primary_range_key' do
72
+ TestReport.primary_range_key(TestReport.primary_key).should match(":primary_range:#{TestReport.primary_key}")
73
+ end
74
+ it 'has range_key' do
75
+ TestReport.range_key.should match(":range")
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,58 @@
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
+ config.pipelined = false
15
+ end
16
+ end
17
+ context 'for class methods' do
18
+ let(:dataset1) {
19
+ [
20
+ {id: 2, s_id: 20, name_1: 'John', name_2: 'Krakis', super_date: Time.new(2002)},
21
+ {id: 2, s_id: 25, name_1: 'Jake', name_2: 'Harken', super_date: Time.new(2003)},
22
+ {id: 3, s_id: 30, name_1: 'Jake', name_2: 'Marken', super_date: Time.new(2004)},
23
+ {id: 3, s_id: 35, name_1: 'Jake', name_2: 'Barken', super_date: Time.new(2005)},
24
+ {id: 3, s_id: 40, name_1: 'Jake', name_2: 'Farken', super_date: Time.new(2006)},
25
+ {id: 3, s_id: 45, name_1: 'Jake', name_2: 'Larken', super_date: Time.new(2007)},
26
+ {id: 3, s_id: 50, name_1: 'Jake', name_2: 'Tarken', super_date: Time.new(2008)},
27
+ ]
28
+ }
29
+ before(:each) do
30
+ class SomeReport < LightStore::Data
31
+ primary_field :id
32
+ date_constraint_field :super_date
33
+ end
34
+ SomeReport.add_data(dataset1)
35
+ end
36
+ after(:each) do
37
+ SomeReport.clear_all
38
+ end
39
+ # make these tests testier
40
+ describe '#add_data' do
41
+ it 'sets data' do
42
+ SomeReport.add_data(dataset1)
43
+ end
44
+ it 'gets data no args' do
45
+ SomeReport.get_data()
46
+ end
47
+ it 'gets data with primary key' do
48
+ SomeReport.get_data(primary_key: 2)
49
+ end
50
+ it 'gets data with date range' do
51
+ SomeReport.get_data(date_range: [2000, '2013'])
52
+ end
53
+ it 'gets data with primary key and date range' do
54
+ SomeReport.get_data(primary_key: 1, date_range: [Time.new(2001), Time.new(2013)])
55
+ end
56
+ end
57
+ end
58
+ end
metadata CHANGED
@@ -1,78 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Michael Kompanets
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-03-26 00:00:00.000000000 Z
11
+ date: 2014-04-29 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '1.3'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
26
  version: '1.3'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faker
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mock_redis
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
60
81
  - !ruby/object:Gem::Version
61
82
  version: '0'
62
83
  - !ruby/object:Gem::Dependency
63
84
  name: redis
64
85
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
86
  requirements:
67
- - - ! '>='
87
+ - - ">="
68
88
  - !ruby/object:Gem::Version
69
89
  version: '0'
70
90
  type: :runtime
71
91
  prerelease: false
72
92
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
93
  requirements:
75
- - - ! '>='
94
+ - - ">="
76
95
  - !ruby/object:Gem::Version
77
96
  version: '0'
78
97
  description: A library for storing data about an object in spreadsheet-like format
@@ -83,41 +102,42 @@ executables: []
83
102
  extensions: []
84
103
  extra_rdoc_files: []
85
104
  files:
86
- - .gitignore
105
+ - ".gitignore"
87
106
  - Gemfile
88
107
  - LICENSE.txt
89
108
  - README.md
90
109
  - Rakefile
91
110
  - lib/light_store.rb
111
+ - lib/light_store/class_accessor_methods.rb
92
112
  - lib/light_store/class_methods.rb
93
113
  - lib/light_store/configuration.rb
94
114
  - lib/light_store/version.rb
95
115
  - light_store.gemspec
96
- - spec/light_store_spec.rb
116
+ - spec/class_accessor_methods_spec.rb
117
+ - spec/class_methods_spec.rb
97
118
  homepage: https://github.com/mkompanets/light_store
98
119
  licenses:
99
120
  - MIT
121
+ metadata: {}
100
122
  post_install_message:
101
123
  rdoc_options: []
102
124
  require_paths:
103
125
  - lib
104
126
  required_ruby_version: !ruby/object:Gem::Requirement
105
- none: false
106
127
  requirements:
107
- - - ! '>='
128
+ - - ">="
108
129
  - !ruby/object:Gem::Version
109
130
  version: '0'
110
131
  required_rubygems_version: !ruby/object:Gem::Requirement
111
- none: false
112
132
  requirements:
113
- - - ! '>='
133
+ - - ">="
114
134
  - !ruby/object:Gem::Version
115
135
  version: '0'
116
136
  requirements: []
117
137
  rubyforge_project:
118
- rubygems_version: 1.8.25
138
+ rubygems_version: 2.2.2
119
139
  signing_key:
120
- specification_version: 3
140
+ specification_version: 4
121
141
  summary: This library aims to provide an easy way to store report data about objects. Data
122
142
  that is typically generated with complex queries and methods could be stored in
123
143
  a flat and accessible format. This is meant to be a general purpose library, but
@@ -125,5 +145,6 @@ summary: This library aims to provide an easy way to store report data about obj
125
145
  from an idea that each 'row' of report data could be identified by the object id
126
146
  and a secondary id relevant to the report.
127
147
  test_files:
128
- - spec/light_store_spec.rb
148
+ - spec/class_accessor_methods_spec.rb
149
+ - spec/class_methods_spec.rb
129
150
  has_rdoc:
@@ -1,299 +0,0 @@
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