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 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
+