filebase 0.3.6 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -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