pobject 0.1.0
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 +7 -0
- data/README.md +128 -0
- data/lib/pobject/pobject.rb +156 -0
- data/lib/pobject/version.rb +3 -0
- data/lib/pobject.rb +2 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 20dc0805c434c64cf463c8d9b466592c07c05f08
|
4
|
+
data.tar.gz: ee6b8c53b78befa5a1ef90e3e74b0a330fecced3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d15a6cbca9c7fdb0af9b99433cea357d1c4c78e696736ed41b5e8d15d260660a2e80a430c83cada6a4f35c4160b7634e2e9e47e69e68a502beb428cb5dd904dd
|
7
|
+
data.tar.gz: 8cd94ca4e4692ddb785c1eaa66821e9ec9eb9462e833d49238bea4ea901aaa037a01d7797c8dabe2bbee29b7fd3b359e703f7ed8ff41e353b692f7c8ed34f7fb
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
Persistent Object
|
2
|
+
==================================================
|
3
|
+
|
4
|
+
[](https://rubygems.org/gems/pobject)
|
5
|
+
[](https://travis-ci.org/DannyBen/pobject)
|
6
|
+
[](https://codeclimate.com/github/DannyBen/pobject)
|
7
|
+
[](https://gemnasium.com/DannyBen/pobject)
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
Transparently persist objects to disk as YAML or PStore files.
|
12
|
+
|
13
|
+
---
|
14
|
+
|
15
|
+
Install
|
16
|
+
--------------------------------------------------
|
17
|
+
|
18
|
+
```
|
19
|
+
$ gem install pobject
|
20
|
+
```
|
21
|
+
|
22
|
+
Or with bundler:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'pobject'
|
26
|
+
```
|
27
|
+
|
28
|
+
Usage
|
29
|
+
--------------------------------------------------
|
30
|
+
|
31
|
+
Your object should inherit from PObject.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'pobject'
|
35
|
+
|
36
|
+
class Settings < PObject
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Now, any time you access a property, it is saved to a file. By default, we will
|
41
|
+
svae a YAML file with the same name as the class:
|
42
|
+
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class Settings < PObject
|
46
|
+
end
|
47
|
+
|
48
|
+
config = Settings.new
|
49
|
+
config.port = 3000
|
50
|
+
# Will create a 'settings.yml' file and store the port value
|
51
|
+
```
|
52
|
+
|
53
|
+
You can access any attributes by either dot notation or hash notation.
|
54
|
+
|
55
|
+
```
|
56
|
+
config.port
|
57
|
+
# => 3000
|
58
|
+
|
59
|
+
config[:port]
|
60
|
+
# => 3000
|
61
|
+
```
|
62
|
+
|
63
|
+
To change the location of the file, simply override the `to_store` method.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class Settings < PObject
|
67
|
+
def to_store
|
68
|
+
"config/local.yml"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
config = Settings.new
|
73
|
+
config.port = 3000
|
74
|
+
# Will create a 'config/local.yml'
|
75
|
+
```
|
76
|
+
|
77
|
+
Whenever you use the `.yml` extension, PObject will store a YAML file. If you
|
78
|
+
wish to store a `PStore` object instead, provide any other extension:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class Settings < PObject
|
82
|
+
def to_store
|
83
|
+
"config/local.pstore"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
To store several objects in one store file, your `to_store` method should
|
89
|
+
return an array with two elements: The first, is the path to the file and
|
90
|
+
the second is any unique key identifying the instance.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class Hero < PObject
|
94
|
+
def initialize(id)
|
95
|
+
@id = id
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_store
|
99
|
+
["heroes.yml", @id]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
hammer = Hero.new :hammer
|
104
|
+
raynor = Hero.new :raynor
|
105
|
+
|
106
|
+
hammer.name = 'Sgt. Hammer'
|
107
|
+
raynor.name = 'Raynor'
|
108
|
+
# =>
|
109
|
+
# ---
|
110
|
+
# :hammer:
|
111
|
+
# :name: Sgt. Hammer
|
112
|
+
# :raynor:
|
113
|
+
# :name: Raynor
|
114
|
+
```
|
115
|
+
|
116
|
+
By default, PObject will raise an error when accessing a property that does
|
117
|
+
not exist. To change this behavior, call `allow_missing` at the beinning of
|
118
|
+
your object
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
class Book < PObject
|
122
|
+
allow_missing
|
123
|
+
end
|
124
|
+
|
125
|
+
book = Book.new
|
126
|
+
book.author
|
127
|
+
# => nil
|
128
|
+
```
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
require 'yaml/store'
|
3
|
+
|
4
|
+
class PObject
|
5
|
+
# When `allow_missing` is called at the inheriting class level, then
|
6
|
+
# all calls to any missing attribute will return `nil` and supress the
|
7
|
+
# normal ruby behavior of raising a `NoMethodError`.
|
8
|
+
def self.allow_missing
|
9
|
+
send :define_method, :allow_missing? do
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Intercept any call to an unknown method, and try to load it from the
|
15
|
+
# store. If it does not exist in the store as well, then raise the usual
|
16
|
+
# error.
|
17
|
+
# This method will also be called when trying to assign to a non existing
|
18
|
+
# attribute. In this case, we will save it to the store.
|
19
|
+
def method_missing(method, args=nil, &_block)
|
20
|
+
if method.to_s[-1] == "="
|
21
|
+
set method.to_s.chomp('=').to_sym, args
|
22
|
+
else
|
23
|
+
content = get method
|
24
|
+
content ? content : allow_missing? ? nil : super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Mirror the method_missing behavior.
|
29
|
+
def respond_to_missing?(method, include_private=false)
|
30
|
+
if method.to_s[-1] == "="
|
31
|
+
true
|
32
|
+
else
|
33
|
+
allow_missing? or !!get(method.to_sym)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Allow hash-style access to any of our stored properties.
|
38
|
+
# This will not reload from the disk more than once.
|
39
|
+
def [](key)
|
40
|
+
get key
|
41
|
+
end
|
42
|
+
|
43
|
+
# Allow hash-style assignment to new peoperties
|
44
|
+
def []=(key, value)
|
45
|
+
set key, value
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return a nice string when inspecting or printing.
|
49
|
+
# This will load values from the store if they were not already loaded.
|
50
|
+
def inspect
|
51
|
+
properties = attributes.map { |var| [var, get(var)].join(":") }.join ', '
|
52
|
+
properties = " #{properties}" unless properties.empty?
|
53
|
+
"<#{self.class.name}#{properties}>"
|
54
|
+
end
|
55
|
+
alias_method :to_s, :inspect
|
56
|
+
|
57
|
+
# Return attribute names, so we can print them and their values in
|
58
|
+
# `inspect`.
|
59
|
+
def attributes
|
60
|
+
@attributes ||= attributes!
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return attribute names from the store.
|
64
|
+
# TODO: Maybe also merge with instance_variables. Pay attention to @s.
|
65
|
+
def attributes!
|
66
|
+
store.transaction { store.roots }.sort
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# We need to have a default `allow_missing?` method, which may be
|
72
|
+
# overridden by calling `allow_missing`.
|
73
|
+
def allow_missing?
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get a value from the store, and set it as an instance variable so that
|
78
|
+
# subsequent calls do not access the disk.
|
79
|
+
def get(key)
|
80
|
+
result = instance_variable_get("@#{key}")
|
81
|
+
result.nil? ? get!(key) : result
|
82
|
+
end
|
83
|
+
|
84
|
+
# Hard get value from the store.
|
85
|
+
def get!(key)
|
86
|
+
result = store.transaction do
|
87
|
+
if store_key
|
88
|
+
store[store_key] ||= {}
|
89
|
+
store[store_key][key]
|
90
|
+
else
|
91
|
+
store[key]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
instance_variable_set "@#{key}", result
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set a key=value pair in the store, and in an instance variable so that
|
98
|
+
# any subsequent call to `get` will not have to read it from the disk.
|
99
|
+
# In addition, add it to the attributes list, which is used by `inspect`.
|
100
|
+
def set(key, value)
|
101
|
+
instance_variable_set "@#{key}", value
|
102
|
+
attributes << key unless attributes.include? key
|
103
|
+
store.transaction do
|
104
|
+
if store_key
|
105
|
+
store[store_key] ||= {}
|
106
|
+
store[store_key][key] = value
|
107
|
+
else
|
108
|
+
store[key] = value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# The default store location. May be overridden by the inheriting class.
|
114
|
+
def to_store
|
115
|
+
"#{self.class.to_s.downcase}.yml"
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return the actual store object. It can either be a YAML::Store or a
|
119
|
+
# PStore, which behave exactly the same.
|
120
|
+
def store
|
121
|
+
@store ||= store!
|
122
|
+
end
|
123
|
+
|
124
|
+
# Hard get the actual store object, based on the provided file extension.
|
125
|
+
def store!
|
126
|
+
if ['.yml', 'yaml'].include?(store_file[-4,4])
|
127
|
+
YAML::Store.new(store_file)
|
128
|
+
else
|
129
|
+
PStore.new(store_file)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return the store filename.
|
134
|
+
def store_file
|
135
|
+
@store_file ||= store_file!
|
136
|
+
end
|
137
|
+
|
138
|
+
# Hard return the store filename based on the value of `to_store`.
|
139
|
+
# If an array, the first element is the path.
|
140
|
+
def store_file!
|
141
|
+
to_store.is_a?(Array) ? to_store[0] : to_store
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return the key to use in case multiple objects are to be saved in the
|
145
|
+
# same file.
|
146
|
+
def store_key
|
147
|
+
@store_key.nil? ? @store_key = store_key! : @store_key
|
148
|
+
end
|
149
|
+
|
150
|
+
# Hard return the store key based on the `to_store` value. If an array,
|
151
|
+
# the second element represents the key.
|
152
|
+
def store_key!
|
153
|
+
to_store.is_a?(Array) ? to_store[1] : false
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
data/lib/pobject.rb
ADDED
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pobject
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danny Ben Shitrit
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: runfile
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: runfile-tasks
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.11'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.11'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '9.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '9.0'
|
83
|
+
description: Auto save objects as files
|
84
|
+
email: db@dannyben.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- README.md
|
90
|
+
- lib/pobject.rb
|
91
|
+
- lib/pobject/pobject.rb
|
92
|
+
- lib/pobject/version.rb
|
93
|
+
homepage: https://github.com/DannyBen/pobject
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 2.0.0
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.5.1
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: Persistent Object
|
117
|
+
test_files: []
|