filebase 0.3.6 → 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/filebase.rb +11 -7
- data/lib/filebase/attributes.rb +25 -25
- data/lib/filebase/drivers/json.rb +52 -0
- data/lib/filebase/drivers/marshal.rb +44 -0
- data/lib/filebase/drivers/mixin.rb +38 -0
- data/lib/filebase/drivers/yaml.rb +45 -0
- data/lib/filebase/model.rb +151 -32
- metadata +18 -5
- data/lib/drivers/yaml.rb +0 -35
data/lib/filebase.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'drivers/
|
2
|
+
require 'filebase/drivers/json'
|
3
|
+
|
3
4
|
class Filebase
|
4
5
|
class << self ; attr_accessor :storage ; end
|
5
|
-
self.storage =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
self.storage = Drivers::JSON
|
7
|
+
end
|
8
|
+
|
9
|
+
class String
|
10
|
+
unless method_defined?(:camel_case)
|
11
|
+
def camel_case
|
12
|
+
gsub(/(_)(\w)/) { $~[2].upcase }.gsub(/^([a-z])/) { $~[1].upcase }
|
13
|
+
end
|
14
|
+
end
|
11
15
|
end
|
data/lib/filebase/attributes.rb
CHANGED
@@ -1,44 +1,44 @@
|
|
1
1
|
module Attributes
|
2
2
|
|
3
|
-
def initialize(
|
3
|
+
def initialize( assigns = {} ) ; self.attributes = assigns ; end
|
4
4
|
|
5
5
|
def attributes=( hash )
|
6
|
-
|
7
|
-
@attrs = hash.inject({}) { |h,p| k,v = p; h[k.to_s] = v; h }
|
6
|
+
@attrs = {}; hash.each { |k,v| @attrs[k.to_s] = v }
|
8
7
|
end
|
9
8
|
|
10
9
|
def attributes ; @attrs ||= {} ; end
|
11
10
|
|
12
|
-
def has_key?( key ) ;
|
11
|
+
def has_key?( key ) ; @attrs.has_key?( key.to_s ) ; end
|
13
12
|
|
14
|
-
def
|
13
|
+
def keys; @attrs.keys; end
|
14
|
+
|
15
|
+
def delete( key ) ; @attrs.delete( key.to_s ) ; end
|
15
16
|
|
16
17
|
def method_missing(name,*args)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
super
|
25
|
-
end
|
26
|
-
else super
|
27
|
-
end
|
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
|
28
25
|
end
|
29
26
|
|
30
|
-
def [](name)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def get(name)
|
37
|
-
( ( rval = attributes[name.to_s] ).is_a?( Hash ) and attributes[name.to_s] = self.class.new( rval ) ) or rval
|
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
|
38
33
|
end
|
39
34
|
|
35
|
+
def []=(name,val) ; @attrs[name.to_s] = val ; end
|
36
|
+
|
40
37
|
def to_h ; @attrs ; end
|
41
38
|
|
42
|
-
|
39
|
+
alias :set :[]=
|
40
|
+
alias :get :[]
|
41
|
+
alias :to_hash :to_h
|
43
42
|
|
43
|
+
|
44
44
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'filebase/drivers/mixin'
|
4
|
+
|
5
|
+
class Filebase
|
6
|
+
module Drivers
|
7
|
+
class JSON
|
8
|
+
include Mixin
|
9
|
+
|
10
|
+
def initialize( root )
|
11
|
+
super
|
12
|
+
@extension = "json"
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
Dir["#{@root}/*.json"].map do |file|
|
17
|
+
obj = File.open(file) { |f| ::JSON.load(f) }
|
18
|
+
obj['key'] = File.basename(file, '.json') if obj.is_a? Hash
|
19
|
+
obj or nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def find( key )
|
24
|
+
obj = File.open( path(key) ) { |f| ::JSON.load(f) } rescue nil
|
25
|
+
obj['key'] = key if obj.is_a? Hash
|
26
|
+
obj or nil # convert false to nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_keys(keys)
|
30
|
+
keys.map do |key|
|
31
|
+
obj = File.open( path(key) ) { |f| ::JSON.load(f) } rescue nil
|
32
|
+
obj['key'] = key if obj.is_a? Hash
|
33
|
+
obj or nil # convert false to nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def write( key, hash )
|
38
|
+
File.open(path(key), "w") do |f|
|
39
|
+
begin
|
40
|
+
f.puts ::JSON.pretty_generate(hash)
|
41
|
+
true
|
42
|
+
rescue
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'filebase/drivers/mixin'
|
3
|
+
|
4
|
+
class Filebase
|
5
|
+
|
6
|
+
module Drivers
|
7
|
+
class Marshal
|
8
|
+
include Mixin
|
9
|
+
|
10
|
+
def initialize( root )
|
11
|
+
super
|
12
|
+
@extension = "marshal"
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
Dir["#{@root}/*.marshal"].map do |file|
|
17
|
+
obj = File.open(file) { |f| ::Marshal.load(f) }
|
18
|
+
obj['key'] = File.basename(file, '.marshal') if obj.is_a? Hash
|
19
|
+
obj or nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def find( key )
|
24
|
+
obj = File.open( path(key) ) { |f| ::Marshal.load(f) } rescue nil
|
25
|
+
obj['key'] = key if obj.is_a? Hash
|
26
|
+
obj or nil # convert false to nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_keys(keys)
|
30
|
+
keys.map do |key|
|
31
|
+
obj = File.open( path(key) ) { |f| ::Marshal.load(f) } rescue nil
|
32
|
+
obj['key'] = key if obj.is_a? Hash
|
33
|
+
obj or nil # convert false to nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def write( key, object )
|
38
|
+
File.open( path(key), "w" ) { |f| ::Marshal.dump(object, f); true }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
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,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'filebase/drivers/mixin'
|
4
|
+
|
5
|
+
class Filebase
|
6
|
+
|
7
|
+
module Drivers
|
8
|
+
class YAML
|
9
|
+
include Mixin
|
10
|
+
|
11
|
+
def initialize( root )
|
12
|
+
super
|
13
|
+
@extension = "yml"
|
14
|
+
end
|
15
|
+
|
16
|
+
def all
|
17
|
+
Dir["#{@root}/*.yml"].map do |file|
|
18
|
+
obj = ::YAML.load_file(file)
|
19
|
+
obj['key'] = File.basename(file, '.yml') if obj.is_a? Hash
|
20
|
+
obj or nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find( key )
|
25
|
+
obj = ::YAML.load_file( path( key ) ) rescue nil
|
26
|
+
obj['key'] = key if obj.is_a? Hash
|
27
|
+
obj or nil # convert false to nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_keys(keys)
|
31
|
+
keys.map do |key|
|
32
|
+
obj = ::YAML.load_file( path( key ) ) rescue nil
|
33
|
+
obj['key'] = key if obj.is_a? Hash
|
34
|
+
obj or nil # convert false to nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def write( key, object )
|
39
|
+
object if File.open( path(key), "w" ) { |f| ::YAML.dump(object, f) }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/filebase/model.rb
CHANGED
@@ -5,13 +5,14 @@ class Filebase
|
|
5
5
|
Error = RuntimeError
|
6
6
|
|
7
7
|
module Model
|
8
|
-
|
9
|
-
def self.[]( path )
|
8
|
+
|
9
|
+
def self.[]( path, driver=nil )
|
10
10
|
Module.new do |mixin|
|
11
11
|
( class << mixin ; self ; end ).module_eval do
|
12
12
|
define_method( :included ) do | model |
|
13
13
|
model.module_eval do
|
14
|
-
|
14
|
+
storage = driver ? Filebase::Drivers.const_get(driver.to_s) : Filebase.storage
|
15
|
+
@db = storage.new(path)
|
15
16
|
extend Mixins::ClassMethods ; include Attributes ; include Mixins::InstanceMethods
|
16
17
|
end
|
17
18
|
end
|
@@ -23,57 +24,175 @@ class Filebase
|
|
23
24
|
|
24
25
|
module ClassMethods
|
25
26
|
attr_accessor :db
|
26
|
-
def create( assigns )
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
+
|
30
57
|
def save( object )
|
31
|
-
|
32
|
-
|
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 )
|
33
61
|
end
|
62
|
+
|
34
63
|
def delete( object )
|
35
|
-
|
36
|
-
|
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 }
|
37
76
|
end
|
38
|
-
|
77
|
+
|
78
|
+
def has_one( assoc_key, options = {} )
|
39
79
|
module_eval do
|
40
|
-
define_method
|
80
|
+
define_method assoc_key do
|
81
|
+
foreign_class = options[:class] || Object.module_eval( assoc_key.to_s.camel_case )
|
41
82
|
@has_one ||= {}
|
42
|
-
|
43
|
-
@has_one[name] ||= options[:class].find( get( name ) )
|
83
|
+
@has_one[assoc_key] ||= foreign_class.find( get( assoc_key ) )
|
44
84
|
end
|
45
|
-
define_method(
|
46
|
-
@has_one ||= {}; @has_one[
|
47
|
-
set(
|
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 )
|
48
88
|
end
|
49
89
|
end
|
50
90
|
end
|
51
|
-
|
91
|
+
|
92
|
+
def has_many( assoc_key, options = {} )
|
52
93
|
module_eval do
|
53
|
-
define_method(
|
94
|
+
define_method( assoc_key ) do
|
95
|
+
foreign_class = options[:class] || Object.module_eval( assoc_key.to_s.camel_case )
|
54
96
|
@has_many ||= {}
|
55
|
-
|
56
|
-
@has_many[name] ||= ( get( name ) || [] ).uniq.map { |key| options[:class].find( key ) }
|
97
|
+
@has_many[assoc_key] ||= ( get( assoc_key ) || [] ).uniq.map { |key| foreign_class.find( key ) }
|
57
98
|
end
|
58
99
|
# when we save, make sure to pick up any changes to the array
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
64
131
|
end
|
65
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
|
66
184
|
end
|
67
185
|
end
|
186
|
+
|
68
187
|
end
|
69
188
|
|
70
189
|
module InstanceMethods
|
71
|
-
def
|
72
|
-
def assign( assigns ) ; assigns.each { |k,v| self.send( "#{k}=", v ) }; self ; end
|
73
|
-
def save ; self.class.save( self ) ; self; end
|
190
|
+
def save ; self.class.save( self ) ; end
|
74
191
|
def delete ; self.class.delete( self ) ; self ; end
|
75
|
-
def ==(object)
|
76
|
-
|
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
|
77
196
|
def hash ; key.hash ; end
|
78
197
|
end
|
79
198
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filebase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Yoder
|
@@ -18,9 +18,19 @@ dependencies:
|
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- - "
|
21
|
+
- - "="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: 0.6.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.7
|
24
34
|
version:
|
25
35
|
description:
|
26
36
|
email: dan@zeraweb.com
|
@@ -31,8 +41,11 @@ extensions: []
|
|
31
41
|
extra_rdoc_files: []
|
32
42
|
|
33
43
|
files:
|
34
|
-
- lib/drivers/yaml.rb
|
35
44
|
- lib/filebase/attributes.rb
|
45
|
+
- lib/filebase/drivers/json.rb
|
46
|
+
- lib/filebase/drivers/marshal.rb
|
47
|
+
- lib/filebase/drivers/mixin.rb
|
48
|
+
- lib/filebase/drivers/yaml.rb
|
36
49
|
- lib/filebase/model.rb
|
37
50
|
- lib/filebase.rb
|
38
51
|
has_rdoc: true
|
@@ -57,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
70
|
requirements: []
|
58
71
|
|
59
72
|
rubyforge_project: filebase
|
60
|
-
rubygems_version: 1.
|
73
|
+
rubygems_version: 1.3.1
|
61
74
|
signing_key:
|
62
75
|
specification_version: 2
|
63
76
|
summary: Simple file-based database with model support.
|
data/lib/drivers/yaml.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'extensions/io'
|
3
|
-
class Filebase
|
4
|
-
|
5
|
-
class YAML
|
6
|
-
|
7
|
-
def initialize( root )
|
8
|
-
@root = root.to_s
|
9
|
-
end
|
10
|
-
|
11
|
-
def path( key )
|
12
|
-
File.join( @root, key.to_s ) + '.yml'
|
13
|
-
end
|
14
|
-
|
15
|
-
def all
|
16
|
-
Dir[ path('*') ].map { |file| find( File.basename(file,'.yml' ) ) }.compact
|
17
|
-
end
|
18
|
-
|
19
|
-
def find( key )
|
20
|
-
obj = ::YAML.load( File.read( path( key ) ) ) rescue nil
|
21
|
-
obj['key'] = key if Hash === obj
|
22
|
-
obj or nil # convert false to nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def save( key, object )
|
26
|
-
File.write( path( key ), object.to_yaml ) and object
|
27
|
-
end
|
28
|
-
|
29
|
-
def delete( key )
|
30
|
-
FileUtils.remove( path( key ) )
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|