waves-filebase 0.3.8
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.
- data/lib/filebase.rb +16 -0
- data/lib/filebase/attributes.rb +44 -0
- data/lib/filebase/drivers/json.rb +50 -0
- data/lib/filebase/drivers/marshal.rb +42 -0
- data/lib/filebase/drivers/mixin.rb +38 -0
- data/lib/filebase/drivers/yaml.rb +43 -0
- data/lib/filebase/model.rb +203 -0
- metadata +68 -0
data/lib/filebase.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'filebase/drivers/mixin'
|
3
|
+
require 'filebase/drivers/json'
|
4
|
+
|
5
|
+
class Filebase
|
6
|
+
class << self ; attr_accessor :storage ; end
|
7
|
+
self.storage = Drivers::JSON
|
8
|
+
end
|
9
|
+
|
10
|
+
class String
|
11
|
+
unless method_defined?(:camel_case)
|
12
|
+
def camel_case
|
13
|
+
gsub(/(_)(\w)/) { $~[2].upcase }.gsub(/^([a-z])/) { $~[1].upcase }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Attributes
|
2
|
+
|
3
|
+
def initialize( assigns = {} ) ; self.attributes = assigns ; end
|
4
|
+
|
5
|
+
def attributes=( hash )
|
6
|
+
@attrs = {}; hash.each { |k,v| @attrs[k.to_s] = v }
|
7
|
+
end
|
8
|
+
|
9
|
+
def attributes ; @attrs ||= {} ; end
|
10
|
+
|
11
|
+
def has_key?( key ) ; @attrs.has_key?( key.to_s ) ; end
|
12
|
+
|
13
|
+
def keys; @attrs.keys; end
|
14
|
+
|
15
|
+
def delete( key ) ; @attrs.delete( key.to_s ) ; end
|
16
|
+
|
17
|
+
def method_missing(name,*args)
|
18
|
+
if args.empty?
|
19
|
+
get(name.to_s)
|
20
|
+
elsif (name = name.to_s) =~ /=$/
|
21
|
+
set(name.chop, args[0])
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](name)
|
28
|
+
if ( rval = @attrs[name = name.to_s] ).is_a?( Hash )
|
29
|
+
@attrs[name] = self.class.new( rval )
|
30
|
+
else
|
31
|
+
rval
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def []=(name,val) ; @attrs[name.to_s] = val ; end
|
36
|
+
|
37
|
+
def to_h ; @attrs ; end
|
38
|
+
|
39
|
+
alias :set :[]=
|
40
|
+
alias :get :[]
|
41
|
+
alias :to_hash :to_h
|
42
|
+
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'fileutils'
|
3
|
+
class Filebase
|
4
|
+
module Drivers
|
5
|
+
class JSON
|
6
|
+
include Mixin
|
7
|
+
|
8
|
+
def initialize( root )
|
9
|
+
super
|
10
|
+
@extension = "json"
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
Dir["#{@root}/*.json"].map do |file|
|
15
|
+
obj = File.open(file) { |f| ::JSON.load(f) }
|
16
|
+
obj['key'] = File.basename(file, '.json') if obj.is_a? Hash
|
17
|
+
obj or nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find( key )
|
22
|
+
obj = File.open( path(key) ) { |f| ::JSON.load(f) } rescue nil
|
23
|
+
obj['key'] = key if obj.is_a? Hash
|
24
|
+
obj or nil # convert false to nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_keys(keys)
|
28
|
+
keys.map do |key|
|
29
|
+
obj = File.open( path(key) ) { |f| ::JSON.load(f) } rescue nil
|
30
|
+
obj['key'] = key if obj.is_a? Hash
|
31
|
+
obj or nil # convert false to nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def write( key, hash )
|
36
|
+
File.open(path(key), "w") do |f|
|
37
|
+
begin
|
38
|
+
f.puts ::JSON.pretty_generate(hash)
|
39
|
+
true
|
40
|
+
rescue
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
class Filebase
|
3
|
+
|
4
|
+
module Drivers
|
5
|
+
class Marshal
|
6
|
+
include Mixin
|
7
|
+
|
8
|
+
def initialize( root )
|
9
|
+
super
|
10
|
+
@extension = "marshal"
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
Dir["#{@root}/*.marshal"].map do |file|
|
15
|
+
obj = File.open(file) { |f| ::Marshal.load(f) }
|
16
|
+
obj['key'] = File.basename(file, '.marshal') if obj.is_a? Hash
|
17
|
+
obj or nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find( key )
|
22
|
+
obj = File.open( path(key) ) { |f| ::Marshal.load(f) } rescue nil
|
23
|
+
obj['key'] = key if obj.is_a? Hash
|
24
|
+
obj or nil # convert false to nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_keys(keys)
|
28
|
+
keys.map do |key|
|
29
|
+
obj = File.open( path(key) ) { |f| ::Marshal.load(f) } rescue nil
|
30
|
+
obj['key'] = key if obj.is_a? Hash
|
31
|
+
obj or nil # convert false to nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def write( key, object )
|
36
|
+
File.open( path(key), "w" ) { |f| ::Marshal.dump(object, f); true }
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
class Filebase
|
3
|
+
module Drivers
|
4
|
+
module Mixin
|
5
|
+
|
6
|
+
def initialize(root)
|
7
|
+
@root = root.to_s
|
8
|
+
FileUtils.mkdir_p(root) unless File.exist?(root)
|
9
|
+
end
|
10
|
+
|
11
|
+
def root
|
12
|
+
@root
|
13
|
+
end
|
14
|
+
|
15
|
+
def path( key )
|
16
|
+
raise Filebase::Error, "can't generate a path using a nil key" unless key
|
17
|
+
"#{@root}/#{key}.#{@extension}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_key?( key )
|
21
|
+
File.exist?( path(key) )
|
22
|
+
end
|
23
|
+
|
24
|
+
def keys
|
25
|
+
Dir["#{@root}/*.#{@extension}"].sort.map { |fname| File.basename(fname, ".#{@extension}") }
|
26
|
+
end
|
27
|
+
|
28
|
+
def count
|
29
|
+
Dir["#{@root}/*.#{@extension}"].size
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete( key )
|
33
|
+
::FileUtils.remove( path( key ) )
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
class Filebase
|
4
|
+
|
5
|
+
module Drivers
|
6
|
+
class YAML
|
7
|
+
include Mixin
|
8
|
+
|
9
|
+
def initialize( root )
|
10
|
+
super
|
11
|
+
@extension = "yml"
|
12
|
+
end
|
13
|
+
|
14
|
+
def all
|
15
|
+
Dir["#{@root}/*.yml"].map do |file|
|
16
|
+
obj = ::YAML.load_file(file)
|
17
|
+
obj['key'] = File.basename(file, '.yml') if obj.is_a? Hash
|
18
|
+
obj or nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find( key )
|
23
|
+
obj = ::YAML.load_file( path( key ) ) rescue nil
|
24
|
+
obj['key'] = key if obj.is_a? Hash
|
25
|
+
obj or nil # convert false to nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_keys(keys)
|
29
|
+
keys.map do |key|
|
30
|
+
obj = ::YAML.load_file( path( key ) ) rescue nil
|
31
|
+
obj['key'] = key if obj.is_a? Hash
|
32
|
+
obj or nil # convert false to nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def write( key, object )
|
37
|
+
object if File.open( path(key), "w" ) { |f| ::YAML.dump(object, f) }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'filebase/attributes'
|
2
|
+
|
3
|
+
class Filebase
|
4
|
+
|
5
|
+
Error = RuntimeError
|
6
|
+
|
7
|
+
module Model
|
8
|
+
|
9
|
+
def self.[]( path, driver=nil )
|
10
|
+
Module.new do |mixin|
|
11
|
+
( class << mixin ; self ; end ).module_eval do
|
12
|
+
define_method( :included ) do | model |
|
13
|
+
model.module_eval do
|
14
|
+
storage = driver ? Filebase::Drivers.const_get(driver.to_s) : Filebase.storage
|
15
|
+
@db = storage.new(path)
|
16
|
+
extend Mixins::ClassMethods ; include Attributes ; include Mixins::InstanceMethods
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Mixins
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
attr_accessor :db
|
27
|
+
def create( assigns )
|
28
|
+
object = new( assigns )
|
29
|
+
raise Filebase::Error, "record already exists" if @db.has_key?(object["key"])
|
30
|
+
save( object )
|
31
|
+
end
|
32
|
+
def all ; @db.all.map { |attrs| new( attrs ) } ; end
|
33
|
+
|
34
|
+
def find( key ) ; attrs = @db.find( key ); new( attrs ) if attrs ; end
|
35
|
+
alias_method :[], :find
|
36
|
+
|
37
|
+
def keys; @db.keys; end
|
38
|
+
|
39
|
+
def find_keys(keys)
|
40
|
+
@db.find_keys(keys).map { |h| new(h) if h }
|
41
|
+
end
|
42
|
+
|
43
|
+
def slice(start, length)
|
44
|
+
k = self.keys.slice(start, length)
|
45
|
+
k ? find_keys(k) : []
|
46
|
+
end
|
47
|
+
|
48
|
+
def reverse_slice(start, length)
|
49
|
+
k = self.keys.reverse.slice(start, length)
|
50
|
+
k ? find_keys(k) : []
|
51
|
+
end
|
52
|
+
|
53
|
+
def count
|
54
|
+
@db.count
|
55
|
+
end
|
56
|
+
|
57
|
+
def save( object )
|
58
|
+
key = object.key
|
59
|
+
raise( Filebase::Error, 'attempted to save an object with nil key' ) unless key and !key.empty?
|
60
|
+
object if @db.write( key, object.to_h )
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete( object )
|
64
|
+
key = object.key
|
65
|
+
raise( Filebase::Error, 'attempted to delete an object with nil key' ) unless key and !key.empty?
|
66
|
+
@db.delete( object.key )
|
67
|
+
end
|
68
|
+
|
69
|
+
def index
|
70
|
+
@index
|
71
|
+
end
|
72
|
+
|
73
|
+
def reindex # Yes, this would take a lot of time.
|
74
|
+
@index.all { |field| field.delete }
|
75
|
+
all.each { |record| record.save }
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_one( assoc_key, options = {} )
|
79
|
+
module_eval do
|
80
|
+
define_method assoc_key do
|
81
|
+
foreign_class = options[:class] || Object.module_eval( assoc_key.to_s.camel_case )
|
82
|
+
@has_one ||= {}
|
83
|
+
@has_one[assoc_key] ||= foreign_class.find( get( assoc_key ) )
|
84
|
+
end
|
85
|
+
define_method( assoc_key.to_s + '=' ) do | val |
|
86
|
+
@has_one ||= {}; @has_one[assoc_key] = nil
|
87
|
+
set( assoc_key, String === val ? val : val.key )
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def has_many( assoc_key, options = {} )
|
93
|
+
module_eval do
|
94
|
+
define_method( assoc_key ) do
|
95
|
+
foreign_class = options[:class] || Object.module_eval( assoc_key.to_s.camel_case )
|
96
|
+
@has_many ||= {}
|
97
|
+
@has_many[assoc_key] ||= ( get( assoc_key ) || [] ).uniq.map { |key| foreign_class.find( key ) }
|
98
|
+
end
|
99
|
+
# when we save, make sure to pick up any changes to the array
|
100
|
+
before_save do |object|
|
101
|
+
object.set( assoc_key, object.send( assoc_key ).map{ |x| x.key }.uniq )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def index_on( attribute, options={} )
|
107
|
+
storage = options[:driver] ? Filebase::Drivers.const_get(options[:driver].to_s) : @db.class
|
108
|
+
field_name = attribute.to_s
|
109
|
+
@index = index ||= storage.new("#{@db.root}/indexes")
|
110
|
+
klass = self
|
111
|
+
|
112
|
+
(class<<self;self;end).module_eval do
|
113
|
+
define_method "find_by_#{field_name}" do |val|
|
114
|
+
ids = index.find(field_name)[val]
|
115
|
+
ids ? ids.map { |id| klass.find(id) } : []
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
after_save do |object|
|
120
|
+
key = object.key
|
121
|
+
field = index.find(field_name) || {}
|
122
|
+
if val = object[field_name]
|
123
|
+
if val.is_a? Array
|
124
|
+
val.each do |v|
|
125
|
+
list = (field[v.to_s] ||= [])
|
126
|
+
list << key unless list.include? key
|
127
|
+
end
|
128
|
+
else
|
129
|
+
list = field[val.to_s] ||= []
|
130
|
+
list << key unless list.include? key
|
131
|
+
end
|
132
|
+
end
|
133
|
+
index.write(field_name, field)
|
134
|
+
end
|
135
|
+
|
136
|
+
before_delete do |object|
|
137
|
+
(field = index.find(field_name)) || return
|
138
|
+
if val = object[field_name]
|
139
|
+
field[val.to_s].delete(object.key)
|
140
|
+
end
|
141
|
+
index.write(field_name, field)
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def before_save(&block)
|
148
|
+
(class<<self;self;end).module_eval do
|
149
|
+
old_save = instance_method(:save)
|
150
|
+
define_method :save do |object|
|
151
|
+
yield object
|
152
|
+
old_save.bind(self).call(object)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def after_save(&block)
|
158
|
+
(class<<self;self;end).module_eval do
|
159
|
+
old_save = instance_method(:save)
|
160
|
+
define_method :save do |object|
|
161
|
+
old_save.bind(self).call(object)
|
162
|
+
object if yield( object )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def before_delete(&block)
|
168
|
+
(class<<self;self;end).module_eval do
|
169
|
+
old_delete = instance_method(:delete)
|
170
|
+
define_method :delete do |object|
|
171
|
+
yield object
|
172
|
+
old_delete.bind(self).call(object)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def after_delete(&block)
|
178
|
+
(class<<self;self;end).module_eval do
|
179
|
+
old_delete = instance_method(:delete)
|
180
|
+
define_method :delete do |object|
|
181
|
+
old_delete.bind(self).call(object)
|
182
|
+
yield object
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
module InstanceMethods
|
190
|
+
def save ; self.class.save( self ) ; end
|
191
|
+
def delete ; self.class.delete( self ) ; self ; end
|
192
|
+
def ==(object)
|
193
|
+
key == object.key if object.is_a? Attributes
|
194
|
+
end
|
195
|
+
def eql?(object) ; key == object.key ; end # this seems iffy
|
196
|
+
def hash ; key.hash ; end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waves-filebase
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Yoder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-04-30 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: extensions
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: dan@zeraweb.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- lib/filebase/attributes.rb
|
35
|
+
- lib/filebase/drivers/json.rb
|
36
|
+
- lib/filebase/drivers/marshal.rb
|
37
|
+
- lib/filebase/drivers/mixin.rb
|
38
|
+
- lib/filebase/drivers/yaml.rb
|
39
|
+
- lib/filebase/model.rb
|
40
|
+
- lib/filebase.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://dev.zeraweb.com/waves
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.8.6
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project: filebase
|
63
|
+
rubygems_version: 1.2.0
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: Simple file-based database with model support.
|
67
|
+
test_files: []
|
68
|
+
|