obstore 0.0.1

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