filebase 0.3.6 → 0.3.10

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.
@@ -1,11 +1,15 @@
1
1
  require 'rubygems'
2
- require 'drivers/yaml'
2
+ require 'filebase/drivers/json'
3
+
3
4
  class Filebase
4
5
  class << self ; attr_accessor :storage ; end
5
- self.storage = YAML
6
- def initialize( root = '.', storage = nil )
7
- @storage = ( storage || Filebase.storage ).new( root )
8
- end
9
- # delegate everything to the storage object ...
10
- def method_missing( name,*args ) ; @storage.send( name, *args ) ; end
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
@@ -1,44 +1,44 @@
1
1
  module Attributes
2
2
 
3
- def initialize( hash = {} ) ; self.attributes = hash ; end
3
+ def initialize( assigns = {} ) ; self.attributes = assigns ; end
4
4
 
5
5
  def attributes=( hash )
6
- # copy the hash, converting all keys to strings
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 ) ; attributes.has_key?( key.to_s ) ; end
11
+ def has_key?( key ) ; @attrs.has_key?( key.to_s ) ; end
13
12
 
14
- def delete( key ) ; attributes.delete( key.to_s ) ; end
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
- name = name.to_s
18
- case args.length
19
- when 0 then get( name )
20
- when 1
21
- if name[-1,1] == '='
22
- set( name[0..-2], args[0] )
23
- else
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) ; get(name) ; end
31
-
32
- def []=(name,val) ; set(name,val) ; end
33
-
34
- def set(name, val) ; attributes[name.to_s] = val ; end
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
- alias_method :to_hash, :to_h
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
@@ -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
- @db = Filebase.new( path )
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 ) ; save( new( assigns ) ) ; end
27
- def all ; db.all.map { |attrs| new( attrs ) } ; end
28
- def find( key ) ; attrs = db.find( key ); new( attrs ) if attrs ; end
29
- def []( key ) ; find( key ) ; end
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
- raise( Filebase::Error.new, 'attempted to save an object with nil key' ) if object.key.nil? or object.key.empty?
32
- db.save( object.key, object.to_h ) and 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 )
33
61
  end
62
+
34
63
  def delete( object )
35
- raise( Filebase::Error.new, 'attempted to delete an object with nil key' ) if object.key.nil? or object.key.empty?
36
- db.delete( object.key )
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
- def has_one( name, options = {} )
77
+
78
+ def has_one( assoc_key, options = {} )
39
79
  module_eval do
40
- define_method name do
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
- options[:class] ||= Object.module_eval( name.to_s.gsub(/(_)(\w)/) { $2.upcase }.gsub(/^([a-z])/) { $1.upcase } )
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( name.to_s + '=' ) do | val |
46
- @has_one ||= {}; @has_one[name] = nil
47
- set( name, String === val ? val : val.key )
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
- def has_many( name, options = {} )
91
+
92
+ def has_many( assoc_key, options = {} )
52
93
  module_eval do
53
- define_method( name ) do
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
- options[:class] ||= Object.module_eval( name.to_s.camel_case )
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
- (class<<self;self;end).module_eval do
60
- old_save = instance_method(:save)
61
- define_method :save do |object|
62
- object.set( name, object.send( name ).map{ |x| x.key }.uniq )
63
- old_save.bind(self).call(object)
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 initialize( assigns ) ; super ; assign( assigns ) ; end
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) ; key == object.key ; end
76
- def eql?(object) ; key == object.key ; 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
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.6
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: "0"
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.2.0
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.
@@ -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