hyperion-api 0.0.1.alpha2 → 0.0.1.alpha3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/hyperion/api.rb +18 -0
- data/lib/hyperion/memory.rb +111 -0
- data/lib/hyperion/util.rb +19 -0
- data/spec/hyperion/api_spec.rb +18 -3
- data/spec/hyperion/fake_ds.rb +41 -40
- data/spec/hyperion/memory_spec.rb +13 -0
- data/spec/hyperion/util_spec.rb +30 -0
- metadata +8 -4
- data/lib/hyperion/dev/memory.rb +0 -113
- data/spec/hyperion/dev/memory_spec.rb +0 -11
data/lib/hyperion/api.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'hyperion/query'
|
2
2
|
require 'hyperion/filter'
|
3
3
|
require 'hyperion/sort'
|
4
|
+
require 'hyperion/util'
|
4
5
|
|
5
6
|
module Hyperion
|
6
7
|
class API
|
@@ -19,6 +20,23 @@ module Hyperion
|
|
19
20
|
Thread.current[:datastore] || raise('No Datastore installed')
|
20
21
|
end
|
21
22
|
|
23
|
+
# Assigns the datastore within the given block
|
24
|
+
def with_datastore(name, opts={})
|
25
|
+
self.datastore = new_datastore(name, opts)
|
26
|
+
yield
|
27
|
+
self.datastore = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def new_datastore(name, opts={})
|
31
|
+
begin
|
32
|
+
require "hyperion/#{name}"
|
33
|
+
rescue LoadError
|
34
|
+
raise "Can't find datastore implementation: #{name}"
|
35
|
+
end
|
36
|
+
ds_klass = Hyperion.const_get(Util.class_name(name.to_s))
|
37
|
+
ds_klass.new(opts)
|
38
|
+
end
|
39
|
+
|
22
40
|
# Saves a record. Any additional parameters will get merged onto the record before it is saved.
|
23
41
|
|
24
42
|
# Hyperion::API.save({:kind => :foo})
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'hyperion/api'
|
2
|
+
|
3
|
+
module Hyperion
|
4
|
+
class Memory
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
@id_counter = 0
|
8
|
+
@store = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def save(records)
|
12
|
+
records.map do |record|
|
13
|
+
key = API.new?(record) ? generate_key : record[:key]
|
14
|
+
record[:key] = key
|
15
|
+
store[key] = record
|
16
|
+
record
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_key(key)
|
21
|
+
store[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(query)
|
25
|
+
records = store.values
|
26
|
+
records = filter_kind(query.kind, records)
|
27
|
+
records = apply_filters(query.filters, records)
|
28
|
+
records = apply_sorts(query.sorts, records)
|
29
|
+
records = apply_offset(query.offset, records)
|
30
|
+
records = apply_limit(query.limit, records)
|
31
|
+
records
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete_by_key(key)
|
35
|
+
store.delete(key)
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(query)
|
40
|
+
records = find(query)
|
41
|
+
store.delete_if do |key, record|
|
42
|
+
records.include?(record)
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def count(query)
|
48
|
+
find(query).length
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :store
|
54
|
+
|
55
|
+
def filter_kind(kind, records)
|
56
|
+
records.select do |record|
|
57
|
+
record[:kind] == kind
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply_filters(filters, records)
|
62
|
+
records.select do |record|
|
63
|
+
filters.all? do |filter|
|
64
|
+
value = record[filter.field]
|
65
|
+
case filter.operator
|
66
|
+
when '<'; value < filter.value
|
67
|
+
when '<='; value <= filter.value
|
68
|
+
when '>'; value > filter.value
|
69
|
+
when '>='; value >= filter.value
|
70
|
+
when '='; value == filter.value
|
71
|
+
when '!='; value != filter.value
|
72
|
+
when 'contains?'; filter.value.include?(value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def apply_sorts(sorts, records)
|
79
|
+
records.sort { |record1, record2| compare_records record1, record2, sorts }
|
80
|
+
end
|
81
|
+
|
82
|
+
def compare_records(record1, record2, sorts)
|
83
|
+
sorts.each do |sort|
|
84
|
+
result = compare_record record1, record2, sort
|
85
|
+
return result if result
|
86
|
+
end
|
87
|
+
0
|
88
|
+
end
|
89
|
+
|
90
|
+
def compare_record(record1, record2, sort)
|
91
|
+
field1, field2 = record1[sort.field], record2[sort.field]
|
92
|
+
field1 == field2 ? nil :
|
93
|
+
field1 < field2 && sort.ascending? ? -1 :
|
94
|
+
field1 > field2 && sort.descending? ? -1 : 1
|
95
|
+
end
|
96
|
+
|
97
|
+
def generate_key
|
98
|
+
@id_counter += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def apply_offset(offset, records)
|
102
|
+
return records unless offset
|
103
|
+
records.drop offset
|
104
|
+
end
|
105
|
+
|
106
|
+
def apply_limit(limit, records)
|
107
|
+
return records unless limit
|
108
|
+
records.take(limit)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hyperion
|
2
|
+
class Util
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def camel_case(str)
|
7
|
+
str.gsub(/[_| |\-][A-Za-z]/) { |a| a[1..-1].upcase }
|
8
|
+
end
|
9
|
+
|
10
|
+
def class_name(str)
|
11
|
+
cameled = camel_case(str)
|
12
|
+
cameled[0] = cameled[0].capitalize
|
13
|
+
cameled
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/spec/hyperion/api_spec.rb
CHANGED
@@ -14,6 +14,16 @@ describe Hyperion::API do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
context 'factory' do
|
18
|
+
it 'bombs on unknown implementations' do
|
19
|
+
expect {api.new_datastore(:unknown)}.to raise_error("Can't find datastore implementation: unknown")
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'creates a memory datastore' do
|
23
|
+
api.new_datastore(:memory).class.should == Hyperion::Memory
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
17
27
|
context 'new?' do
|
18
28
|
it 'false if a record exists' do
|
19
29
|
api.new?({:key => 1}).should be_false
|
@@ -27,9 +37,14 @@ describe Hyperion::API do
|
|
27
37
|
context 'with fake datastore' do
|
28
38
|
attr_reader :fake_ds
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
def fake_ds
|
41
|
+
Thread.current[:datastore]
|
42
|
+
end
|
43
|
+
|
44
|
+
around :each do |example|
|
45
|
+
api.with_datastore(:fake_ds) do
|
46
|
+
example.run
|
47
|
+
end
|
33
48
|
end
|
34
49
|
|
35
50
|
context 'save' do
|
data/spec/hyperion/fake_ds.rb
CHANGED
@@ -1,43 +1,44 @@
|
|
1
|
-
|
1
|
+
module Hyperion
|
2
|
+
class FakeDs
|
3
|
+
|
4
|
+
attr_accessor :saved_records, :queries, :key_queries, :returns
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
@saved_records = []
|
8
|
+
@returns = []
|
9
|
+
@queries = []
|
10
|
+
@key_queries = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def save(records)
|
14
|
+
@saved_records += records
|
15
|
+
returns.shift || []
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_by_key(key)
|
19
|
+
@key_queries << key
|
20
|
+
returns.shift || nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(query)
|
24
|
+
@queries << query
|
25
|
+
returns.shift || []
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_by_key(key)
|
29
|
+
@key_queries << key
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(query)
|
34
|
+
@queries << query
|
35
|
+
returns.shift || nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def count(query)
|
39
|
+
@queries << query
|
40
|
+
returns.shift || 0
|
41
|
+
end
|
2
42
|
|
3
|
-
attr_accessor :saved_records, :queries, :key_queries, :returns
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@saved_records = []
|
7
|
-
@returns = []
|
8
|
-
@queries = []
|
9
|
-
@key_queries = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def save(records)
|
13
|
-
@saved_records += records
|
14
|
-
returns.shift || []
|
15
|
-
end
|
16
|
-
|
17
|
-
def find_by_key(key)
|
18
|
-
@key_queries << key
|
19
|
-
returns.shift || nil
|
20
|
-
end
|
21
|
-
|
22
|
-
def find(query)
|
23
|
-
@queries << query
|
24
|
-
returns.shift || []
|
25
43
|
end
|
26
|
-
|
27
|
-
def delete_by_key(key)
|
28
|
-
@key_queries << key
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
|
32
|
-
def delete(query)
|
33
|
-
@queries << query
|
34
|
-
returns.shift || nil
|
35
|
-
end
|
36
|
-
|
37
|
-
def count(query)
|
38
|
-
@queries << query
|
39
|
-
returns.shift || 0
|
40
|
-
end
|
41
|
-
|
42
44
|
end
|
43
|
-
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hyperion/util'
|
2
|
+
|
3
|
+
describe Hyperion::Util do
|
4
|
+
|
5
|
+
def util
|
6
|
+
Hyperion::Util
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'camel cases words' do
|
10
|
+
util.camel_case('fake_ds').should == 'fakeDs'
|
11
|
+
util.camel_case('defaultSceneName').should == 'defaultSceneName'
|
12
|
+
util.camel_case('set defaultSceneName').should == 'setDefaultSceneName'
|
13
|
+
util.camel_case('class_name').should == 'className'
|
14
|
+
util.camel_case('once_upon_a_time').should == 'onceUponATime'
|
15
|
+
util.camel_case('with spaces').should == 'withSpaces'
|
16
|
+
util.camel_case('with-dash').should == 'withDash'
|
17
|
+
util.camel_case('starting Capital').should == 'startingCapital'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'converts to class name' do
|
21
|
+
util.class_name('fake_ds').should == 'FakeDs'
|
22
|
+
util.class_name('defaultSceneName').should == 'DefaultSceneName'
|
23
|
+
util.class_name('set defaultSceneName').should == 'SetDefaultSceneName'
|
24
|
+
util.class_name('class_name').should == 'ClassName'
|
25
|
+
util.class_name('once_upon_a_time').should == 'OnceUponATime'
|
26
|
+
util.class_name('with spaces').should == 'WithSpaces'
|
27
|
+
util.class_name('with-dash').should == 'WithDash'
|
28
|
+
util.class_name('starting Capital').should == 'StartingCapital'
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyperion-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.alpha3
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -46,20 +46,23 @@ dependencies:
|
|
46
46
|
description: A Generic Persistence API for Ruby
|
47
47
|
email:
|
48
48
|
- myles@8thlight.com
|
49
|
+
- skim@8thlight.com
|
49
50
|
executables: []
|
50
51
|
extensions: []
|
51
52
|
extra_rdoc_files: []
|
52
53
|
files:
|
53
|
-
- lib/hyperion/dev/memory.rb
|
54
54
|
- lib/hyperion/dev/ds_spec.rb
|
55
|
+
- lib/hyperion/util.rb
|
55
56
|
- lib/hyperion/filter.rb
|
56
57
|
- lib/hyperion/api.rb
|
57
58
|
- lib/hyperion/key.rb
|
59
|
+
- lib/hyperion/memory.rb
|
58
60
|
- lib/hyperion/sort.rb
|
59
61
|
- lib/hyperion/query.rb
|
60
|
-
- spec/hyperion/
|
62
|
+
- spec/hyperion/util_spec.rb
|
61
63
|
- spec/hyperion/key_spec.rb
|
62
64
|
- spec/hyperion/shared_examples.rb
|
65
|
+
- spec/hyperion/memory_spec.rb
|
63
66
|
- spec/hyperion/api_spec.rb
|
64
67
|
- spec/hyperion/fake_ds.rb
|
65
68
|
homepage: https://github.com/mylesmegyesi/hyperion-ruby
|
@@ -88,8 +91,9 @@ signing_key:
|
|
88
91
|
specification_version: 3
|
89
92
|
summary: A Generic Persistence API for Ruby
|
90
93
|
test_files:
|
91
|
-
- spec/hyperion/
|
94
|
+
- spec/hyperion/util_spec.rb
|
92
95
|
- spec/hyperion/key_spec.rb
|
93
96
|
- spec/hyperion/shared_examples.rb
|
97
|
+
- spec/hyperion/memory_spec.rb
|
94
98
|
- spec/hyperion/api_spec.rb
|
95
99
|
- spec/hyperion/fake_ds.rb
|
data/lib/hyperion/dev/memory.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
require 'hyperion/api'
|
2
|
-
|
3
|
-
module Hyperion
|
4
|
-
module Dev
|
5
|
-
class Memory
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@id_counter = 0
|
9
|
-
@store = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def save(records)
|
13
|
-
records.map do |record|
|
14
|
-
key = API.new?(record) ? generate_key : record[:key]
|
15
|
-
record[:key] = key
|
16
|
-
store[key] = record
|
17
|
-
record
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def find_by_key(key)
|
22
|
-
store[key]
|
23
|
-
end
|
24
|
-
|
25
|
-
def find(query)
|
26
|
-
records = store.values
|
27
|
-
records = filter_kind(query.kind, records)
|
28
|
-
records = apply_filters(query.filters, records)
|
29
|
-
records = apply_sorts(query.sorts, records)
|
30
|
-
records = apply_offset(query.offset, records)
|
31
|
-
records = apply_limit(query.limit, records)
|
32
|
-
records
|
33
|
-
end
|
34
|
-
|
35
|
-
def delete_by_key(key)
|
36
|
-
store.delete(key)
|
37
|
-
nil
|
38
|
-
end
|
39
|
-
|
40
|
-
def delete(query)
|
41
|
-
records = find(query)
|
42
|
-
store.delete_if do |key, record|
|
43
|
-
records.include?(record)
|
44
|
-
end
|
45
|
-
nil
|
46
|
-
end
|
47
|
-
|
48
|
-
def count(query)
|
49
|
-
find(query).length
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
attr_reader :store
|
55
|
-
|
56
|
-
def filter_kind(kind, records)
|
57
|
-
records.select do |record|
|
58
|
-
record[:kind] == kind
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def apply_filters(filters, records)
|
63
|
-
records.select do |record|
|
64
|
-
filters.all? do |filter|
|
65
|
-
value = record[filter.field]
|
66
|
-
case filter.operator
|
67
|
-
when '<'; value < filter.value
|
68
|
-
when '<='; value <= filter.value
|
69
|
-
when '>'; value > filter.value
|
70
|
-
when '>='; value >= filter.value
|
71
|
-
when '='; value == filter.value
|
72
|
-
when '!='; value != filter.value
|
73
|
-
when 'contains?'; filter.value.include?(value)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def apply_sorts(sorts, records)
|
80
|
-
records.sort { |record1, record2| compare_records record1, record2, sorts }
|
81
|
-
end
|
82
|
-
|
83
|
-
def compare_records(record1, record2, sorts)
|
84
|
-
sorts.each do |sort|
|
85
|
-
result = compare_record record1, record2, sort
|
86
|
-
return result if result
|
87
|
-
end
|
88
|
-
0
|
89
|
-
end
|
90
|
-
|
91
|
-
def compare_record(record1, record2, sort)
|
92
|
-
field1, field2 = record1[sort.field], record2[sort.field]
|
93
|
-
field1 == field2 ? nil :
|
94
|
-
field1 < field2 && sort.ascending? ? -1 :
|
95
|
-
field1 > field2 && sort.descending? ? -1 : 1
|
96
|
-
end
|
97
|
-
|
98
|
-
def generate_key
|
99
|
-
@id_counter += 1
|
100
|
-
end
|
101
|
-
|
102
|
-
def apply_offset(offset, records)
|
103
|
-
return records unless offset
|
104
|
-
records.drop offset
|
105
|
-
end
|
106
|
-
|
107
|
-
def apply_limit(limit, records)
|
108
|
-
return records unless limit
|
109
|
-
records.take(limit)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|