perobs 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
  - - '>='