obstore 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 189bceb51e975e4cee66730f5eda40d6d2e5f70a
4
+ data.tar.gz: 6064191922ce26a55d7b2cb386e37ee066e31ac2
5
+ SHA512:
6
+ metadata.gz: 2bfaf6545bec341b3d79bf94592dd94ad275e1d0e2e53e533a827c7bff55fe14f4db796f0040e4b0ac636744ec73f10002d8c82b6f09a5807322bee586cf8787
7
+ data.tar.gz: ecf26c593a83171c30090ab75c8c846942b87b6181083bbcaf3decec610b272bf80b27cf1657ef8aa53b0306034067a8f53a4c14dfabc2387b80b034aebdf51c
@@ -0,0 +1,92 @@
1
+ # This provides the data object that obstore uses to save your data
2
+ # and retrieve your data with metadata
3
+ #
4
+ # Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
5
+ # Copyright:: Copyright (c) 2014 Michael Heijmans
6
+ # License:: MIT
7
+
8
+ require 'yaml'
9
+ require 'obstore/lockable'
10
+
11
+ module ObStore
12
+ class Data
13
+
14
+ include ObStore::Lockable
15
+
16
+ attr_accessor :expiry
17
+ attr_reader :updated, :data
18
+
19
+ def initialize(data=nil, options={})
20
+ @expiry = options[:expiry] # in seconds
21
+ @data = {}
22
+ store_data_by_key :data, data
23
+ @updated = Time.now
24
+ if options[:metadata]
25
+ options[:metadata].each do |key, value|
26
+ store_data_by_key key, value
27
+ end
28
+ end
29
+ end
30
+
31
+ # returns the object you had saved
32
+ def fetch
33
+ fetch_data_by_key(:data)
34
+ end
35
+
36
+ # returns boolean value if data has expired
37
+ def stale?
38
+ if @expiry
39
+ if ts < Time.now.to_i - @expiry
40
+ return true
41
+ end
42
+ end
43
+ return false
44
+ end
45
+
46
+ # Saves the object
47
+ def save(data)
48
+ self.data=data
49
+ end
50
+
51
+ # custom setter for data attribute that sets the update time
52
+ def data=(data)
53
+ store_data_by_key(:data, data)
54
+ end
55
+
56
+ # custom getter for retrieving data from the data hash
57
+ def data
58
+ fetch_data_by_key(:data)
59
+ end
60
+
61
+ # helper method to return the timestamp as an int
62
+ def ts
63
+ with_mutex { @updated.to_i }
64
+ end
65
+
66
+ private
67
+
68
+ # method used by method_missing to store meta data
69
+ def store_data_by_key(key, value)
70
+ with_mutex {
71
+ @data.store key.to_sym, value
72
+ @updated = Time.now
73
+ }
74
+ end
75
+
76
+ # method used by method_missing to fetch meta data
77
+ def fetch_data_by_key(key)
78
+ with_mutex { @data[key.to_sym] }
79
+ end
80
+
81
+ def method_missing(meth, *args, &block)
82
+ if meth.to_s =~ /^(.+)=$/
83
+ store_data_by_key($1, *args)
84
+ elsif meth.to_s =~ /^(.+)$/
85
+ fetch_data_by_key($1)
86
+ else
87
+ super
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,91 @@
1
+ # Provides the wrapper for pstore
2
+ #
3
+ # Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
4
+ # Copyright:: Copyright (c) 2014 Michael Heijmans
5
+ # License:: MIT
6
+
7
+ require 'pstore'
8
+ require 'yaml'
9
+ require 'obstore/data'
10
+
11
+ module ObStore
12
+ class FileStore
13
+
14
+ attr_accessor :store
15
+
16
+ def initialize(opts={})
17
+ opts[:database] ||= "./tmp/obstore.db"
18
+ opts[:threadsafe] ||= true
19
+ opts[:atomic_writes] ||= false
20
+ @store = PStore.new(opts[:database], opts[:threadsafe])
21
+ @store.ultra_safe = opts[:atomic_writes]
22
+ end
23
+
24
+ # removes stale records from the pstore db
25
+ def compact!
26
+ keys = []
27
+ @store.transaction do
28
+ keys = @store.roots
29
+ end
30
+ keys.each do |key|
31
+ fetch_data_by_key key.to_sym # just fetching the stale items deletes them
32
+ end
33
+ return true
34
+ end
35
+
36
+ private
37
+
38
+ # marshals the data object to a string
39
+ def marshal(obj)
40
+ YAML.dump obj
41
+ end
42
+
43
+ # un-marshals the passed string into an object
44
+ def unmarshal(str)
45
+ YAML.load str
46
+ end
47
+
48
+ # method used by method_missing to store data
49
+ def store_data_by_key(key, args)
50
+ options = {}
51
+ if args.class == Array
52
+ value = args.shift
53
+ options = args.shift
54
+ else
55
+ value = args
56
+ end
57
+ @store.transaction do
58
+ if value.nil?
59
+ @store.delete key.to_sym
60
+ else
61
+ @store[key.to_sym] = marshal ObStore::Data.new(value, options)
62
+ end
63
+ @store.commit
64
+ end
65
+ end
66
+
67
+ # method used by method_missing to fetch data
68
+ def fetch_data_by_key(key)
69
+ @store.transaction do
70
+ data = unmarshal(@store[key.to_sym])
71
+ if data.stale?
72
+ data = nil
73
+ @store.delete key.to_sym
74
+ @store.commit
75
+ end
76
+ return data
77
+ end
78
+ end
79
+
80
+ def method_missing(meth, *args, &block)
81
+ if meth.to_s =~ /^(.+)=$/
82
+ store_data_by_key($1, *args)
83
+ elsif meth.to_s =~ /^(.+)$/
84
+ fetch_data_by_key($1)
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,21 @@
1
+ # Provides semaphore support to an object
2
+ #
3
+ # Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
4
+ # Copyright:: Copyright (c) 2014 Michael Heijmans
5
+ # License:: MIT
6
+
7
+ require 'thread'
8
+
9
+ module ObStore
10
+ module Lockable
11
+
12
+ def mutex
13
+ @mutex ||= Mutex.new
14
+ end
15
+
16
+ def with_mutex
17
+ mutex.synchronize { yield }
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # Provides the Version Constant
2
+ #
3
+ # Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
4
+ # Copyright:: Copyright (c) 2014 Michael Heijmans
5
+ # License:: MIT
6
+
7
+ module ObStore
8
+ VERSION = "0.0.1"
9
+ end
data/lib/obstore.rb ADDED
@@ -0,0 +1,28 @@
1
+ # This library allows you to persist objects with meta data
2
+ # ObStore supports data expiry and is designed with thread safety
3
+ #
4
+ # Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
5
+ # Copyright:: Copyright (c) 2014 Michael Heijmans
6
+ # License:: MIT
7
+
8
+ module ObStore
9
+
10
+ # require library file that was passed
11
+ def self.require_lib(lib)
12
+ require lib
13
+ end
14
+
15
+ # iterates through the passed in array of
16
+ # library paths and requires each of them
17
+ def self.require_libs(libs)
18
+ libs.each do |lib|
19
+ self.require_lib(lib)
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ $:.concat [File.expand_path('../', __FILE__),File.expand_path('../obstore', __FILE__)]
26
+
27
+ # Require all ruby files in the obstore directory
28
+ ObStore.require_libs Dir.glob(File.expand_path('../obstore', __FILE__) + '/*.rb')
@@ -0,0 +1,101 @@
1
+ require 'obstore/data'
2
+
3
+ describe ObStore::Data do
4
+ before(:all) do
5
+ begin
6
+ Dir.mkdir './tmp'
7
+ rescue
8
+ end
9
+ end
10
+
11
+ after(:all) do
12
+ begin
13
+ File.delete('./tmp/obstore.db')
14
+ rescue
15
+ end
16
+ end
17
+
18
+ describe "#initialize(data=nil, options={})" do
19
+ it 'initalizes an object with nil data' do
20
+ expect(ObStore::Data.new.data).to eq(nil)
21
+ end
22
+ it 'lets you initialize it with a passed in object' do
23
+ expect(ObStore::Data.new(Hash.new).data.class).to eq(Hash)
24
+ end
25
+ it 'sets a nil expiry by default' do
26
+ expect(ObStore::Data.new.expiry).to eq(nil)
27
+ end
28
+ it 'lets you set expiry' do
29
+ expect(ObStore::Data.new(Hash.new, {:expiry=>300}).expiry).to eq(300)
30
+ end
31
+ it 'lets you set any extra metadata you want' do
32
+ expect(ObStore::Data.new(Hash.new, {:metadata=>{:foo=>"bar"}}).foo).to eq("bar")
33
+ end
34
+ end
35
+
36
+ describe "#fetch" do
37
+ it 'returns the object we are tracking' do
38
+ expect(ObStore::Data.new(Hash.new).fetch.class).to eq(Hash)
39
+ end
40
+ end
41
+
42
+ describe "#save(data)" do
43
+ it 'is a wrapper that sets the data we are tracking' do
44
+ o = ObStore::Data.new(Hash.new)
45
+ expect(o.fetch.class).to eq(Hash)
46
+ o.save "test"
47
+ expect(o.fetch).to eq("test")
48
+ end
49
+ end
50
+
51
+ describe "#data=(data)" do
52
+ before(:each) do
53
+ @o = ObStore::Data.new("test")
54
+ end
55
+
56
+ it 'sets the data we are tracking' do
57
+ expect(@o.fetch).to eq("test")
58
+ @o.data = "test2"
59
+ expect(@o.fetch).to eq("test2")
60
+ end
61
+
62
+ it 'updates the timestamp along with the save' do
63
+ ts = @o.ts
64
+ sleep 1
65
+ @o.data = "test2"
66
+ expect(@o.ts).not_to eq(ts)
67
+ end
68
+ end
69
+
70
+ describe "#stale?" do
71
+ before(:each) do
72
+ @o = ObStore::Data.new("test")
73
+ end
74
+ it 'returns false if there is no expiry set' do
75
+ expect(@o.stale?).to eq(false)
76
+ end
77
+ it 'returns false if the data is not stale' do
78
+ @o.expiry = 300
79
+ expect(@o.stale?).to eq(false)
80
+ end
81
+ it 'return true if the data is stale' do
82
+ @o.expiry = 1
83
+ sleep 2
84
+ expect(@o.stale?).to eq(true)
85
+ end
86
+ end
87
+
88
+ describe "#data" do
89
+ it 'returns the object we are tracking' do
90
+ expect(ObStore::Data.new("test").data).to eq("test")
91
+ end
92
+ end
93
+
94
+ describe "#ts" do
95
+ it 'returns the int version of the timestamp' do
96
+ expect(ObStore::Data.new("test").ts.class).to eq(Fixnum)
97
+ end
98
+ end
99
+
100
+
101
+ end
@@ -0,0 +1,103 @@
1
+ require 'obstore/filestore'
2
+
3
+ describe ObStore::FileStore do
4
+ before(:all) do
5
+ begin
6
+ Dir.mkdir './tmp'
7
+ rescue
8
+ end
9
+ end
10
+
11
+ after(:all) do
12
+ begin
13
+ File.delete('./tmp/obstore.db')
14
+ rescue
15
+ end
16
+ end
17
+
18
+ describe "#initialize(options)" do
19
+ it 'initializes a storage object' do
20
+ s = ObStore::FileStore.new
21
+ expect(s.store.path).to eq("./tmp/obstore.db")
22
+ expect(s.store.ultra_safe).to eq(false)
23
+ end
24
+ it 'sets pstore to be ultra_safe when setting atomic_writes' do
25
+ s = ObStore::FileStore.new(:atomic_writes=>true)
26
+ expect(s.store.ultra_safe).to eq(true)
27
+ end
28
+ end
29
+
30
+ describe "#data=(foo)" do
31
+ before(:each) do
32
+ @s = ObStore::FileStore.new
33
+ end
34
+
35
+ it 'allows you to set values for keys using the dot operator' do
36
+ @s.data = "foo"
37
+ expect(@s.data.fetch).to eq("foo")
38
+ end
39
+
40
+ it 'allows you to set expiry for the value' do
41
+ @s.data = Hash.new, {:expiry=>300}
42
+ expect(@s.data.expiry).to eq(300)
43
+ end
44
+
45
+ it 'allows you to pass a metadata hash' do
46
+ @s.data = {:foo=>"bar"}, {:metadata=>{:baz=>"foo"}}
47
+ expect(@s.data.baz).to eq("foo")
48
+ end
49
+
50
+ it 'allows you to set expiry and metadata' do
51
+ @s.data = {:foo=>"bar"}, {:expiry=>600, :metadata=>{:baz=>"foo"}}
52
+ expect(@s.data.expiry).to eq(600)
53
+ expect(@s.data.baz).to eq("foo")
54
+ end
55
+
56
+ it 'returns nil if the data has expired' do
57
+ @s.data = {:foo=>"bar"}, {:expiry=>-3}
58
+ expect(@s.data).to eq(nil)
59
+ end
60
+
61
+ it 'removes the data from the file if the data is stale' do
62
+ @s.data = {:foo=>"bar"}, {:expiry=>-3}
63
+ expect(@s.data).to eq(nil)
64
+ @s.store.transaction do
65
+ expect(@s.store.root?(:data)).to eq(false)
66
+ end
67
+ end
68
+
69
+ it 'removes the object from the store when you nil it out' do
70
+ @s.data = "foo"
71
+ expect(@s.data.fetch).to eq("foo")
72
+ @s.data = nil
73
+ @s.store.transaction do
74
+ expect(@s.store.root?(:data)).to eq(false)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "#compact!" do
80
+ before(:each) do
81
+ @s = ObStore::FileStore.new
82
+ @s.data = {:foo=>"bar"}, {:expiry=>-3}
83
+ @s.more_data = {:foo=>"bar"}, {:expiry=>-3}
84
+ @s.keep = {:foo=>"bar"}, {:expiry=>300}
85
+ @s.never = {:foo=>"bar"}
86
+ end
87
+
88
+ it 'removes all expired records from the file' do
89
+ @s.store.transaction do
90
+ expect(@s.store.roots.length).to eq(4)
91
+ end
92
+ @s.compact!
93
+ @s.store.transaction do
94
+ expect(@s.store.roots.length).to eq(2)
95
+ expect(@s.store.root?(:keep)).to eq(true)
96
+ expect(@s.store.root?(:never)).to eq(true)
97
+ expect(@s.store.root?(:data)).to eq(false)
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,7 @@
1
+ require 'obstore/version'
2
+
3
+ describe 'ObStore::VERSION' do
4
+ it 'returns a version string' do
5
+ expect(ObStore::VERSION.class).to eq(String)
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # This file is loaded before rspec tests are run
2
+ require 'simplecov'
3
+ require 'simplecov-rcov'
4
+ require 'coveralls'
5
+
6
+ SimpleCov.start do
7
+ add_filter '/spec/'
8
+ add_group 'lib', 'lib'
9
+ #SimpleCov.minimum_coverage 75
10
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
11
+ SimpleCov::Formatter::HTMLFormatter,
12
+ SimpleCov::Formatter::RcovFormatter
13
+ ]
14
+ end if ENV["COVERAGE"]
15
+
16
+ Coveralls.wear!
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obstore
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Heijmans
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ description: ObStore allows you to save any object to a file along with metadata about
28
+ that object. You can also set an expiry for an object and ObStore will lazily delete
29
+ the data for you.
30
+ email: parabuzzle@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/obstore.rb
36
+ - lib/obstore/data.rb
37
+ - lib/obstore/filestore.rb
38
+ - lib/obstore/lockable.rb
39
+ - lib/obstore/version.rb
40
+ - spec/lib/data_spec.rb
41
+ - spec/lib/filestore_spec.rb
42
+ - spec/lib/version_spec.rb
43
+ - spec/spec_helper.rb
44
+ homepage: https://github.com/parabuzzle/obstore
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.2.2
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: ObStore is a smart persistent Object store.
68
+ test_files:
69
+ - spec/lib/data_spec.rb
70
+ - spec/lib/filestore_spec.rb
71
+ - spec/lib/version_spec.rb
72
+ - spec/spec_helper.rb