perobs 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 059ab4702a14c6ff6328881c528eda77d321de51
4
- data.tar.gz: 5a8944222bf1feccb2eda749f05d963598e3b945
3
+ metadata.gz: e8576de664206470a3b075101951c4a65144d643
4
+ data.tar.gz: 26a46db076eea54ca4dbb5ed6312a3ec855971d7
5
5
  SHA512:
6
- metadata.gz: eeab29c68225efd8efbfb6a94b14c708fac38e16c6c1a399b55941e11e7dab83ecea925382180b803a7a152b71509eef30c50e40e568d5c9900c0dea1eec1d7f
7
- data.tar.gz: 993b3ff327426b2797e4696a43ad0a0483d45a22638241fe34409b8fe4bbdddbe5a27adb0f06b088ae5f45638515fe11aa0b4f1baf6079afbfde4d7c06188ea5
6
+ metadata.gz: fac5c8714eba1be5beb6647330044177ea4eff24ada10243e0a3a4dc595eb7316662a4fdf0e63e8faf07f9b57eff15d232e66a63a4d3a84019d1b8b2d5a08b70
7
+ data.tar.gz: 93a75aeb2430c57535f6a4fb792db48717c1278d22939be9168e9341c4dddd94565de6adca739ebde76b6d72845f1800394b0aa8ed24da7488f6f10c1772a539
data/README.md CHANGED
@@ -60,7 +60,7 @@ class Person < PEROBS::Object
60
60
  def initialize(store, name)
61
61
  super
62
62
  attr_init(:name, name)
63
- attr_init(:kids, PEROBS::Array.new)
63
+ attr_init(:kids, PEROBS::Array.new(store))
64
64
  end
65
65
 
66
66
  def to_s
@@ -71,9 +71,9 @@ class Person < PEROBS::Object
71
71
  end
72
72
 
73
73
  store = PEROBS::Store.new('family')
74
- store['grandpa'] = joe = Person.new('Joe')
75
- store['grandma'] = jane = Person.new('Jane')
76
- jim = Person.new('Jim')
74
+ store['grandpa'] = joe = Person.new(store, 'Joe')
75
+ store['grandma'] = jane = Person.new(store, 'Jane')
76
+ jim = Person.new(store, 'Jim')
77
77
  jim.father = joe
78
78
  joe.kids << jim
79
79
  jim.mother = jane
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  #
3
- # = BTreeBlob.rb -- Persistent Ruby Object Store
3
+ # = BTreeDB.rb -- Persistent Ruby Object Store
4
4
  #
5
5
  # Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
6
6
  #
@@ -25,6 +25,8 @@
25
25
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
26
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
27
 
28
+ require 'fileutils'
29
+
28
30
  require 'perobs/DataBase'
29
31
  require 'perobs/BTreeBlob'
30
32
 
@@ -88,6 +90,10 @@ module PEROBS
88
90
  @dir_mask = 2 ** @dir_bits - 1
89
91
  end
90
92
 
93
+ def BTreeDB::delete_db(db_name)
94
+ FileUtils.rm_rf(db_name)
95
+ end
96
+
91
97
  # Return true if the object with given ID exists
92
98
  # @param id [Fixnum or Bignum]
93
99
  def include?(id)
@@ -0,0 +1,250 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = DynamoDB.rb -- Persistent Ruby Object Store
4
+ #
5
+ # Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
6
+ #
7
+ # MIT License
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ require 'aws-sdk-core'
29
+
30
+ require 'perobs/DataBase'
31
+ require 'perobs/BTreeBlob'
32
+
33
+ module PEROBS
34
+
35
+ # This class implements an Amazon DynamoDB storage engine for PEROBS.
36
+ class DynamoDB < DataBase
37
+
38
+ # Create a new DynamoDB object.
39
+ # @param db_name [String] name of the DB directory
40
+ # @param options [Hash] options to customize the behavior. Currently only
41
+ # the following options are supported:
42
+ # :serializer : Can be :json and :yaml
43
+ # :aws_id : AWS credentials ID
44
+ # :aws_key : AWS credentials key
45
+ # :aws_region : AWS region to host the data
46
+ def initialize(db_name, options = {})
47
+ # :marshal serialization results in a binary format that cannot easily
48
+ # be stored in DynamoDB. We fall back to :yaml.
49
+ if options[:serializer] == :marshal
50
+ options[:serializer] = :yaml
51
+ end
52
+
53
+ super(options[:serializer] || :json)
54
+
55
+ if options.include?(:aws_id) && options.include?(:aws_key)
56
+ Aws.config[:credentials] = Aws::Credentials.new(options[:aws_id],
57
+ options[:aws_key])
58
+ end
59
+ if options.include?(:aws_region)
60
+ Aws.config[:region] = options[:aws_region]
61
+ end
62
+
63
+ @dynamodb = Aws::DynamoDB::Client.new
64
+ @table_name = db_name
65
+ ensure_table_exists(@table_name)
66
+
67
+ # Read the existing DB config.
68
+ @config = get_hash('config')
69
+ check_option('serializer')
70
+ put_hash('config', @config)
71
+ end
72
+
73
+ def DynamoDB::delete_db(table_name)
74
+ dynamodb = Aws::DynamoDB::Client.new
75
+ dynamodb.delete_table(:table_name => table_name)
76
+ dynamodb.wait_until(:table_not_exists, table_name: table_name)
77
+ end
78
+
79
+ # Return true if the object with given ID exists
80
+ # @param id [Fixnum or Bignum]
81
+ def include?(id)
82
+ dynamo_get_item(id.to_s)
83
+ end
84
+
85
+ # Store a simple Hash as a JSON encoded file into the DB directory.
86
+ # @param name [String] Name of the hash. Will be used as file name.
87
+ # @param hash [Hash] A Hash that maps String objects to strings or
88
+ # numbers.
89
+ def put_hash(name, hash)
90
+ dynamo_put_item(name, hash.to_json)
91
+ end
92
+
93
+ # Load the Hash with the given name.
94
+ # @param name [String] Name of the hash.
95
+ # @return [Hash] A Hash that maps String objects to strings or numbers.
96
+ def get_hash(name)
97
+ if (item = dynamo_get_item(name))
98
+ JSON.parse(item)
99
+ else
100
+ ::Hash.new
101
+ end
102
+ end
103
+
104
+ # Store the given object into the cluster files.
105
+ # @param obj [Hash] Object as defined by PEROBS::ObjectBase
106
+ def put_object(obj, id)
107
+ dynamo_put_item(id.to_s, serialize(obj))
108
+ end
109
+
110
+ # Load the given object from the filesystem.
111
+ # @param id [Fixnum or Bignum] object ID
112
+ # @return [Hash] Object as defined by PEROBS::ObjectBase or nil if ID does
113
+ # not exist
114
+ def get_object(id)
115
+ (item = dynamo_get_item(id.to_s)) ? deserialize(item) : nil
116
+ end
117
+
118
+ # This method must be called to initiate the marking process.
119
+ def clear_marks
120
+ each_item do |id|
121
+ dynamo_mark_item(id, false)
122
+ end
123
+ # Mark the 'config' item so it will not get deleted.
124
+ dynamo_mark_item('config')
125
+ end
126
+
127
+ # Permanently delete all objects that have not been marked. Those are
128
+ # orphaned and are no longer referenced by any actively used object.
129
+ def delete_unmarked_objects
130
+ each_item do |id|
131
+ dynamo_delete_item(id) unless dynamo_is_marked?(id)
132
+ end
133
+ end
134
+
135
+ # Mark an object.
136
+ # @param id [Fixnum or Bignum] ID of the object to mark
137
+ def mark(id)
138
+ dynamo_mark_item(id.to_s, true)
139
+ end
140
+
141
+ # Check if the object is marked.
142
+ # @param id [Fixnum or Bignum] ID of the object to check
143
+ def is_marked?(id)
144
+ dynamo_is_marked?(id.to_s)
145
+ end
146
+
147
+ # Basic consistency check.
148
+ # @param repair [TrueClass/FalseClass] True if found errors should be
149
+ # repaired.
150
+ def check_db(repair = false)
151
+ # TODO: See if we can add checks here
152
+ end
153
+
154
+ # Check if the stored object is syntactically correct.
155
+ # @param id [Fixnum/Bignum] Object ID
156
+ # @param repair [TrueClass/FalseClass] True if an repair attempt should be
157
+ # made.
158
+ # @return [TrueClass/FalseClass] True if the object is OK, otherwise
159
+ # false.
160
+ def check(id, repair)
161
+ begin
162
+ get_object(id)
163
+ rescue => e
164
+ $stderr.puts "Cannot read object with ID #{id}: #{e.message}"
165
+ return false
166
+ end
167
+
168
+ true
169
+ end
170
+
171
+ private
172
+
173
+ def ensure_table_exists(table_name)
174
+ begin
175
+ @dynamodb.describe_table(:table_name => table_name)
176
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
177
+ @dynamodb.create_table(
178
+ :table_name => table_name,
179
+ :attribute_definitions => [
180
+ {
181
+ :attribute_name => :Id,
182
+ :attribute_type => :S
183
+ }
184
+ ],
185
+ :key_schema => [
186
+ {
187
+ :attribute_name => :Id,
188
+ :key_type => :HASH
189
+ }
190
+ ],
191
+ :provisioned_throughput => {
192
+ :read_capacity_units => 1,
193
+ :write_capacity_units => 1,
194
+ }
195
+ )
196
+
197
+ @dynamodb.wait_until(:table_exists, table_name: table_name)
198
+ end
199
+ end
200
+
201
+ def dynamo_get_item(id)
202
+ resp = @dynamodb.get_item(:table_name => @table_name,
203
+ :key => { :Id => id })
204
+ resp[:item] ? resp[:item]['Value'] : nil
205
+ end
206
+
207
+ def dynamo_put_item(id, value)
208
+ @dynamodb.put_item(:table_name => @table_name,
209
+ :item => { :Id => id, :Value => value })
210
+ end
211
+
212
+ def dynamo_delete_item(id)
213
+ @dynamodb.delete_item(:table_name => @table_name,
214
+ :key => { :Id => id })
215
+ end
216
+
217
+ def dynamo_mark_item(id, set_mark = true)
218
+ @dynamodb.update_item(:table_name => @table_name,
219
+ :key => { :Id => id },
220
+ :attribute_updates => {
221
+ :Mark => { :value => set_mark,
222
+ :action => "PUT" }})
223
+ end
224
+
225
+ def dynamo_is_marked?(id)
226
+ resp = @dynamodb.get_item(:table_name => @table_name,
227
+ :key => { :Id => id })
228
+ resp[:item] && resp[:item]['Mark']
229
+ end
230
+
231
+ def each_item
232
+ start_key = nil
233
+ loop do
234
+ resp = @dynamodb.scan(:table_name => @table_name,
235
+ :exclusive_start_key => start_key)
236
+ break if resp.count <= 0
237
+
238
+ resp.items.each do |item|
239
+ yield(item['Id'])
240
+ end
241
+
242
+ break unless resp.last_evaluated_key
243
+ start_key = resp.last_evaluated_key['AttributeName']
244
+ end
245
+ end
246
+
247
+ end
248
+
249
+ end
250
+
@@ -1,4 +1,4 @@
1
1
  module PEROBS
2
2
  # The version number
3
- VERSION = "1.0.0"
3
+ VERSION = "1.0.1"
4
4
  end
data/perobs.gemspec CHANGED
@@ -16,6 +16,7 @@ GEM_SPEC = Gem::Specification.new do |spec|
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
+ spec.required_ruby_version = '>=2.0'
19
20
 
20
21
  spec.add_development_dependency 'bundler', '~> 1.6'
21
22
  spec.add_development_dependency 'yard', '~>0.8.7'
data/spec/BTreeDB_spec.rb CHANGED
@@ -23,24 +23,19 @@
23
23
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
24
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
25
 
26
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
27
-
28
- require 'fileutils'
29
26
  require 'time'
30
27
 
28
+ require 'spec_helper'
31
29
  require 'perobs/BTreeDB'
32
30
 
33
31
  describe PEROBS::BTreeDB do
34
32
 
35
- class UStruct < Struct.new(:first, :second, :third)
36
- end
37
-
38
33
  before(:all) do
39
- FileUtils.rm_rf('fs_test')
34
+ PEROBS::BTreeDB::delete_db('fs_test')
40
35
  end
41
36
 
42
37
  after(:each) do
43
- FileUtils.rm_rf('fs_test')
38
+ PEROBS::BTreeDB::delete_db('fs_test')
44
39
  end
45
40
 
46
41
  it 'should create database' do
@@ -93,7 +88,7 @@ describe PEROBS::BTreeDB do
93
88
  @db.include?(0).should be_true
94
89
  @db.check(0, false).should be_true
95
90
  @db.get_object(0).should == h
96
- FileUtils.rm_rf('fs_test')
91
+ PEROBS::BTreeDB::delete_db('fs_test')
97
92
  end
98
93
  end
99
94
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Schlaeger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-06 00:00:00.000000000 Z
11
+ date: 2015-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,6 +71,7 @@ files:
71
71
  - lib/perobs/Cache.rb
72
72
  - lib/perobs/ClassMap.rb
73
73
  - lib/perobs/DataBase.rb
74
+ - lib/perobs/DynamoDB.rb
74
75
  - lib/perobs/Hash.rb
75
76
  - lib/perobs/Object.rb
76
77
  - lib/perobs/ObjectBase.rb
@@ -100,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - '>='
102
103
  - !ruby/object:Gem::Version
103
- version: '0'
104
+ version: '2.0'
104
105
  required_rubygems_version: !ruby/object:Gem::Requirement
105
106
  requirements:
106
107
  - - '>='