ydb 0.0.1

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.
Files changed (5) hide show
  1. data/README +333 -0
  2. data/Rakefile +390 -0
  3. data/lib/ydb.rb +333 -0
  4. data/ydb.gemspec +30 -0
  5. metadata +85 -0
data/README ADDED
@@ -0,0 +1,333 @@
1
+ # a little wrapper on yaml/store to give collection and record access to a
2
+ # transaction yaml store.
3
+ #
4
+ # sample usage
5
+ #
6
+ # require 'ydb'
7
+ #
8
+ # ydb = YDB.new
9
+ #
10
+ # collection = ydb.collection(:posts)
11
+ #
12
+ # 2.times do
13
+ # id = collection.create(:k => :v, :array => [0,1,2], :time => Time.now.to_f)
14
+ # record = collection.find(id)
15
+ #
16
+ # p record
17
+ # #=> {"k"=>:v, "time"=>1315493211.86451, "id"=>"1", "array"=>[0, 1, 2]}
18
+ # #=> {"k"=>:v, "time"=>1315493211.88372, "id"=>"2", "array"=>[0, 1, 2]}
19
+ # end
20
+ #
21
+ # p collection.all
22
+ # #=> [{"k"=>:v, "time"=>1315493211.86451, "array"=>[0, 1, 2], "id"=>"1"}
23
+ # #=> , {"k"=>:v, "time"=>1315493211.88372, "array"=>[0, 1, 2], "id"=>"2"}
24
+ # #=> ]
25
+ #
26
+ #
27
+ # ydb[:tablename].create(:foo => :bar)
28
+ #
29
+ # puts IO.read(ydb.path)
30
+ # #=> ---
31
+ # #=> tablename:
32
+ # #=> "1":
33
+ # #=> foo: :bar
34
+ # #=> id: "1"
35
+ # #=> posts:
36
+ # #=> "1":
37
+ # #=> k: :v
38
+ # #=> time: 1315493211.86451
39
+ # #=> id: "1"
40
+ # #=> array:
41
+ # #=> - 0
42
+ # #=> - 1
43
+ # #=> - 2
44
+ # #=> "2":
45
+ # #=> k: :v
46
+ # #=> time: 1315493211.88372
47
+ # #=> id: "2"
48
+ # #=> array:
49
+ # #=> - 0
50
+ # #=> - 1
51
+ # #=> - 2
52
+
53
+ require 'yaml/store'
54
+ require 'fileutils'
55
+
56
+ class YDB
57
+ Version = '0.0.1' unless defined?(Version)
58
+
59
+ def YDB.version
60
+ YDB::Version
61
+ end
62
+
63
+ def YDB.dependencies
64
+ {
65
+ 'map' => [ 'map' , '~> 4.4.0' ],
66
+ }
67
+ end
68
+
69
+ def YDB.libdir(*args, &block)
70
+ @libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
71
+ args.empty? ? @libdir : File.join(@libdir, *args)
72
+ ensure
73
+ if block
74
+ begin
75
+ $LOAD_PATH.unshift(@libdir)
76
+ block.call()
77
+ ensure
78
+ $LOAD_PATH.shift()
79
+ end
80
+ end
81
+ end
82
+
83
+ def YDB.load(*libs)
84
+ libs = libs.join(' ').scan(/[^\s+]+/)
85
+ YDB.libdir{ libs.each{|lib| Kernel.load(lib) } }
86
+ end
87
+
88
+ # gems
89
+ #
90
+ begin
91
+ require 'rubygems'
92
+ rescue LoadError
93
+ nil
94
+ end
95
+
96
+ if defined?(gem)
97
+ YDB.dependencies.each do |lib, dependency|
98
+ gem(*dependency) if defined?(gem)
99
+ require(lib)
100
+ end
101
+ end
102
+
103
+ attr_accessor :path
104
+
105
+ def initialize(*args)
106
+ options = Map.options_for!(args)
107
+ @path = ( args.shift || options[:path] || YDB.default_path ).to_s
108
+ FileUtils.mkdir_p(File.dirname(@path)) rescue nil
109
+ end
110
+
111
+ def rm_f
112
+ FileUtils.rm_f(@path) rescue nil
113
+ end
114
+
115
+ def rm_rf
116
+ FileUtils.rm_rf(@path) rescue nil
117
+ end
118
+
119
+ def truncate
120
+ rm_f
121
+ end
122
+
123
+ def ydb
124
+ self
125
+ end
126
+
127
+ def ystore
128
+ @ystore ||= YAML::Store.new(path)
129
+ end
130
+
131
+ class Collection
132
+ def initialize(name, ydb)
133
+ @name = name.to_s
134
+ @ydb = ydb
135
+ end
136
+
137
+ def save(data = {})
138
+ @ydb.save(@name, data)
139
+ end
140
+ alias_method(:create, :save)
141
+ alias_method(:update, :save)
142
+
143
+ def find(id = :all)
144
+ @ydb.find(@name, id)
145
+ end
146
+
147
+ def all
148
+ find(:all)
149
+ end
150
+
151
+ def [](id)
152
+ find(id)
153
+ end
154
+
155
+ def []=(id, data = {})
156
+ data.delete(:id)
157
+ data.delete('id')
158
+ data[:id] = id
159
+ save(data)
160
+ end
161
+
162
+ def delete(id)
163
+ @ydb.delete(@name, id)
164
+ id
165
+ end
166
+ alias_method('destroy', 'delete')
167
+
168
+ def to_hash
169
+ transaction{|y| y[@name]}
170
+ end
171
+
172
+ def size
173
+ to_hash.size
174
+ end
175
+ alias_method('count', 'size')
176
+
177
+ def to_yaml(*args, &block)
178
+ Hash.new.update(to_hash).to_yaml(*args, &block)
179
+ end
180
+
181
+ def transaction(*args, &block)
182
+ @ydb.ystore.transaction(*args, &block)
183
+ end
184
+
185
+ end
186
+
187
+ def collection(name)
188
+ Collection.new(name, ydb)
189
+ end
190
+ alias_method('[]', 'collection')
191
+
192
+ def method_missing(method, *args, &block)
193
+ if args.empty? and block.nil?
194
+ return self.collection(method)
195
+ end
196
+ super
197
+ end
198
+
199
+ def transaction(*args, &block)
200
+ ystore.transaction(*args, &block)
201
+ end
202
+
203
+ def save(collection, data)
204
+ data = data_for(data)
205
+ ystore.transaction do |y|
206
+ collection = (y[collection.to_s] ||= {})
207
+ id = next_id_for(collection, data)
208
+ collection[id] = data
209
+ record = collection[id]
210
+ id
211
+ end
212
+ end
213
+
214
+ def data_for(data)
215
+ data ? Map.for(data) : nil
216
+ end
217
+
218
+ alias_method(:create, :save)
219
+
220
+ def find(collection, id = :all, &block)
221
+ ystore.transaction do |y|
222
+ collection = (y[collection.to_s] ||= {})
223
+ if id.nil? or id == :all
224
+ list = collection.values.map{|data| data_for(data)}
225
+ if block
226
+ collection[:all] = list.map{|record| data_for(block.call(record))}
227
+ else
228
+ list
229
+ end
230
+ else
231
+ key = String(id)
232
+ record = data_for(collection[key])
233
+ if block
234
+ collection[key] = data_for(block.call(record))
235
+ else
236
+ record
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ def update(collection, id = :all, updates = {})
243
+ data = data_for(data)
244
+ find(collection, id) do |record|
245
+ record.update(updates)
246
+ end
247
+ end
248
+
249
+ def delete(collection, id = :all)
250
+ ystore.transaction do |y|
251
+ collection = (y[collection.to_s] ||= {})
252
+ if id.nil? or id == :all
253
+ collection.clear()
254
+ else
255
+ deleted = collection.delete(String(id))
256
+ data_for(deleted) if deleted
257
+ end
258
+ end
259
+ end
260
+ alias_method('destroy', 'delete')
261
+
262
+ def next_id_for(collection, data)
263
+ data = data_for(data)
264
+ begin
265
+ id = id_for(data)
266
+ raise if id.strip.empty?
267
+ id
268
+ rescue
269
+ data['id'] = String(collection.size + 1)
270
+ id_for(data)
271
+ end
272
+ end
273
+
274
+ def id_for(data)
275
+ data = data_for(data)
276
+ %w( id _id ).each{|key| return String(data[key]) if data.has_key?(key)}
277
+ raise("no id discoverable for #{ data.inspect }")
278
+ end
279
+
280
+ def to_hash
281
+ ystore.transaction do |y|
282
+ y.roots.inject(Hash.new){|h,k| h.update(k => y[k])}
283
+ end
284
+ end
285
+
286
+ def to_yaml(*args, &block)
287
+ to_hash.to_yaml(*args, &block)
288
+ end
289
+
290
+ class << YDB
291
+ attr_writer :root
292
+ attr_writer :instance
293
+
294
+ def default_root()
295
+ defined?(Rails.root) && Rails.root ? File.join(Rails.root.to_s, 'db') : '.'
296
+ end
297
+
298
+ def default_path()
299
+ File.join(default_root, 'ydb.yml')
300
+ end
301
+
302
+ def method_missing(method, *args, &block)
303
+ super unless instance.respond_to?(method)
304
+ instance.send(method, *args, &block)
305
+ end
306
+
307
+ def instance
308
+ @instance ||= YDB.new(YDB.default_path)
309
+ end
310
+
311
+ def root
312
+ @root ||= default_root
313
+ end
314
+
315
+ def tmp(&block)
316
+ require 'tempfile' unless defined?(Tempfile)
317
+ tempfile = Tempfile.new("#{ Process.pid }-#{ Process.ppid }-#{ Time.now.to_f }-#{ rand }")
318
+ path = tempfile.path
319
+ ydb = new(:path => path)
320
+ if block
321
+ begin
322
+ block.call(ydb)
323
+ ensure
324
+ ydb.rm_rf
325
+ end
326
+ else
327
+ ydb
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ Ydb = YDb = YDB
@@ -0,0 +1,390 @@
1
+ This.rubyforge_project = 'codeforpeople'
2
+ This.author = "Ara T. Howard"
3
+ This.email = "ara.t.howard@gmail.com"
4
+ This.homepage = "https://github.com/ahoward/#{ This.lib }"
5
+
6
+
7
+ task :default do
8
+ puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
9
+ end
10
+
11
+ task :test do
12
+ run_tests!
13
+ end
14
+
15
+ namespace :test do
16
+ task(:unit){ run_tests!(:unit) }
17
+ task(:functional){ run_tests!(:functional) }
18
+ task(:integration){ run_tests!(:integration) }
19
+ end
20
+
21
+ def run_tests!(which = nil)
22
+ which ||= '**'
23
+ test_dir = File.join(This.dir, "test")
24
+ test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
25
+ test_rbs = Dir.glob(test_glob).sort
26
+
27
+ div = ('=' * 119)
28
+ line = ('-' * 119)
29
+
30
+ test_rbs.each_with_index do |test_rb, index|
31
+ testno = index + 1
32
+ command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }"
33
+
34
+ puts
35
+ say(div, :color => :cyan, :bold => true)
36
+ say("@#{ testno } => ", :bold => true, :method => :print)
37
+ say(command, :color => :cyan, :bold => true)
38
+ say(line, :color => :cyan, :bold => true)
39
+
40
+ system(command)
41
+
42
+ say(line, :color => :cyan, :bold => true)
43
+
44
+ status = $?.exitstatus
45
+
46
+ if status.zero?
47
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
48
+ say("SUCCESS", :color => :green, :bold => true)
49
+ else
50
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
51
+ say("FAILURE", :color => :red, :bold => true)
52
+ end
53
+ say(line, :color => :cyan, :bold => true)
54
+
55
+ exit(status) unless status.zero?
56
+ end
57
+ end
58
+
59
+
60
+ task :gemspec do
61
+ ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem']
62
+ ignore_directories = ['pkg']
63
+ ignore_files = ['test/log', 'a.rb'] + Dir['db/*'] + %w'db'
64
+
65
+ shiteless =
66
+ lambda do |list|
67
+ list.delete_if do |entry|
68
+ next unless test(?e, entry)
69
+ extension = File.basename(entry).split(%r/[.]/).last
70
+ ignore_extensions.any?{|ext| ext === extension}
71
+ end
72
+ list.delete_if do |entry|
73
+ next unless test(?d, entry)
74
+ dirname = File.expand_path(entry)
75
+ ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
76
+ end
77
+ list.delete_if do |entry|
78
+ next unless test(?f, entry)
79
+ filename = File.expand_path(entry)
80
+ ignore_files.any?{|file| File.expand_path(file) == filename}
81
+ end
82
+ end
83
+
84
+ lib = This.lib
85
+ object = This.object
86
+ version = This.version
87
+ files = shiteless[Dir::glob("**/**")]
88
+ executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
89
+ #has_rdoc = true #File.exist?('doc')
90
+ test_files = test(?e, "test/#{ lib }.rb") ? "test/#{ lib }.rb" : nil
91
+ summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
92
+ description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
93
+
94
+ if This.extensions.nil?
95
+ This.extensions = []
96
+ extensions = This.extensions
97
+ %w( Makefile configure extconf.rb ).each do |ext|
98
+ extensions << ext if File.exists?(ext)
99
+ end
100
+ end
101
+ extensions = [extensions].flatten.compact
102
+
103
+ # TODO
104
+ if This.dependencies.nil?
105
+ dependencies = []
106
+ else
107
+ case This.dependencies
108
+ when Hash
109
+ dependencies = This.dependencies.values
110
+ when Array
111
+ dependencies = This.dependencies
112
+ end
113
+ end
114
+
115
+ template =
116
+ if test(?e, 'gemspec.erb')
117
+ Template{ IO.read('gemspec.erb') }
118
+ else
119
+ Template {
120
+ <<-__
121
+ ## <%= lib %>.gemspec
122
+ #
123
+
124
+ Gem::Specification::new do |spec|
125
+ spec.name = <%= lib.inspect %>
126
+ spec.version = <%= version.inspect %>
127
+ spec.platform = Gem::Platform::RUBY
128
+ spec.summary = <%= lib.inspect %>
129
+ spec.description = <%= description.inspect %>
130
+
131
+ spec.files =\n<%= files.sort.pretty_inspect %>
132
+ spec.executables = <%= executables.inspect %>
133
+
134
+ spec.require_path = "lib"
135
+
136
+ spec.test_files = <%= test_files.inspect %>
137
+
138
+ <% dependencies.each do |lib_version| %>
139
+ spec.add_dependency(*<%= Array(lib_version).flatten.inspect %>)
140
+ <% end %>
141
+
142
+ spec.extensions.push(*<%= extensions.inspect %>)
143
+
144
+ spec.rubyforge_project = <%= This.rubyforge_project.inspect %>
145
+ spec.author = <%= This.author.inspect %>
146
+ spec.email = <%= This.email.inspect %>
147
+ spec.homepage = <%= This.homepage.inspect %>
148
+ end
149
+ __
150
+ }
151
+ end
152
+
153
+ Fu.mkdir_p(This.pkgdir)
154
+ gemspec = "#{ lib }.gemspec"
155
+ open(gemspec, "w"){|fd| fd.puts(template)}
156
+ This.gemspec = gemspec
157
+ end
158
+
159
+ task :gem => [:clean, :gemspec] do
160
+ Fu.mkdir_p(This.pkgdir)
161
+ before = Dir['*.gem']
162
+ cmd = "gem build #{ This.gemspec }"
163
+ `#{ cmd }`
164
+ after = Dir['*.gem']
165
+ gem = ((after - before).first || after.first) or abort('no gem!')
166
+ Fu.mv(gem, This.pkgdir)
167
+ This.gem = File.join(This.pkgdir, File.basename(gem))
168
+ end
169
+
170
+ task :readme do
171
+ samples = ''
172
+ prompt = '~ > '
173
+ lib = This.lib
174
+ version = This.version
175
+
176
+ Dir['sample*/*'].sort.each do |sample|
177
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
178
+
179
+ cmd = "cat #{ sample }"
180
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
181
+ samples << Util.indent(`#{ cmd }`, 4) << "\n"
182
+
183
+ cmd = "ruby #{ sample }"
184
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
185
+
186
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
187
+ samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
188
+ end
189
+
190
+ template =
191
+ if test(?e, 'readme.erb')
192
+ Template{ IO.read('readme.erb') }
193
+ else
194
+ Template {
195
+ <<-__
196
+ NAME
197
+ #{ lib }
198
+
199
+ DESCRIPTION
200
+
201
+ INSTALL
202
+ gem install #{ lib }
203
+
204
+ SAMPLES
205
+ #{ samples }
206
+ __
207
+ }
208
+ end
209
+
210
+ open("README", "w"){|fd| fd.puts template}
211
+ end
212
+
213
+
214
+ task :clean do
215
+ Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
216
+ end
217
+
218
+
219
+ task :release => [:clean, :gemspec, :gem] do
220
+ gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
221
+ raise "which one? : #{ gems.inspect }" if gems.size > 1
222
+ raise "no gems?" if gems.size < 1
223
+
224
+ cmd = "gem push #{ This.gem }"
225
+ puts cmd
226
+ puts
227
+ system(cmd)
228
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
229
+
230
+ cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
231
+ puts cmd
232
+ puts
233
+ system(cmd)
234
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
235
+ end
236
+
237
+
238
+
239
+
240
+
241
+ BEGIN {
242
+ # support for this rakefile
243
+ #
244
+ $VERBOSE = nil
245
+
246
+ require 'ostruct'
247
+ require 'erb'
248
+ require 'fileutils'
249
+ require 'rbconfig'
250
+ require 'pp'
251
+
252
+ # fu shortcut
253
+ #
254
+ Fu = FileUtils
255
+
256
+ # cache a bunch of stuff about this rakefile/environment
257
+ #
258
+ This = OpenStruct.new
259
+
260
+ This.file = File.expand_path(__FILE__)
261
+ This.dir = File.dirname(This.file)
262
+ This.pkgdir = File.join(This.dir, 'pkg')
263
+
264
+ # grok lib
265
+ #
266
+ lib = ENV['LIB']
267
+ unless lib
268
+ lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
269
+ end
270
+ This.lib = lib
271
+
272
+ # grok version
273
+ #
274
+ version = ENV['VERSION']
275
+ unless version
276
+ require "./lib/#{ This.lib }"
277
+ This.name = lib.capitalize
278
+ This.object = eval(This.name)
279
+ version = This.object.send(:version)
280
+ end
281
+ This.version = version
282
+
283
+ # see if dependencies are export by the module
284
+ #
285
+ if This.object.respond_to?(:dependencies)
286
+ This.dependencies = This.object.dependencies
287
+ end
288
+
289
+ # we need to know the name of the lib an it's version
290
+ #
291
+ abort('no lib') unless This.lib
292
+ abort('no version') unless This.version
293
+
294
+ # discover full path to this ruby executable
295
+ #
296
+ c = Config::CONFIG
297
+ bindir = c["bindir"] || c['BINDIR']
298
+ ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
299
+ ruby_ext = c['EXEEXT'] || ''
300
+ ruby = File.join(bindir, (ruby_install_name + ruby_ext))
301
+ This.ruby = ruby
302
+
303
+ # some utils
304
+ #
305
+ module Util
306
+ def indent(s, n = 2)
307
+ s = unindent(s)
308
+ ws = ' ' * n
309
+ s.gsub(%r/^/, ws)
310
+ end
311
+
312
+ def unindent(s)
313
+ indent = nil
314
+ s.each_line do |line|
315
+ next if line =~ %r/^\s*$/
316
+ indent = line[%r/^\s*/] and break
317
+ end
318
+ indent ? s.gsub(%r/^#{ indent }/, "") : s
319
+ end
320
+ extend self
321
+ end
322
+
323
+ # template support
324
+ #
325
+ class Template
326
+ def initialize(&block)
327
+ @block = block
328
+ @template = block.call.to_s
329
+ end
330
+ def expand(b=nil)
331
+ ERB.new(Util.unindent(@template)).result((b||@block).binding)
332
+ end
333
+ alias_method 'to_s', 'expand'
334
+ end
335
+ def Template(*args, &block) Template.new(*args, &block) end
336
+
337
+ # colored console output support
338
+ #
339
+ This.ansi = {
340
+ :clear => "\e[0m",
341
+ :reset => "\e[0m",
342
+ :erase_line => "\e[K",
343
+ :erase_char => "\e[P",
344
+ :bold => "\e[1m",
345
+ :dark => "\e[2m",
346
+ :underline => "\e[4m",
347
+ :underscore => "\e[4m",
348
+ :blink => "\e[5m",
349
+ :reverse => "\e[7m",
350
+ :concealed => "\e[8m",
351
+ :black => "\e[30m",
352
+ :red => "\e[31m",
353
+ :green => "\e[32m",
354
+ :yellow => "\e[33m",
355
+ :blue => "\e[34m",
356
+ :magenta => "\e[35m",
357
+ :cyan => "\e[36m",
358
+ :white => "\e[37m",
359
+ :on_black => "\e[40m",
360
+ :on_red => "\e[41m",
361
+ :on_green => "\e[42m",
362
+ :on_yellow => "\e[43m",
363
+ :on_blue => "\e[44m",
364
+ :on_magenta => "\e[45m",
365
+ :on_cyan => "\e[46m",
366
+ :on_white => "\e[47m"
367
+ }
368
+ def say(phrase, *args)
369
+ options = args.last.is_a?(Hash) ? args.pop : {}
370
+ options[:color] = args.shift.to_s.to_sym unless args.empty?
371
+ keys = options.keys
372
+ keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
373
+
374
+ color = options[:color]
375
+ bold = options.has_key?(:bold)
376
+
377
+ parts = [phrase]
378
+ parts.unshift(This.ansi[color]) if color
379
+ parts.unshift(This.ansi[:bold]) if bold
380
+ parts.push(This.ansi[:clear]) if parts.size > 1
381
+
382
+ method = options[:method] || :puts
383
+
384
+ Kernel.send(method, parts.join)
385
+ end
386
+
387
+ # always run out of the project dir
388
+ #
389
+ Dir.chdir(This.dir)
390
+ }
@@ -0,0 +1,333 @@
1
+ # a little wrapper on yaml/store to give collection and record access to a
2
+ # transaction yaml store.
3
+ #
4
+ # sample usage
5
+ #
6
+ # require 'ydb'
7
+ #
8
+ # ydb = YDB.new
9
+ #
10
+ # collection = ydb.collection(:posts)
11
+ #
12
+ # 2.times do
13
+ # id = collection.create(:k => :v, :array => [0,1,2], :time => Time.now.to_f)
14
+ # record = collection.find(id)
15
+ #
16
+ # p record
17
+ # #=> {"k"=>:v, "time"=>1315493211.86451, "id"=>"1", "array"=>[0, 1, 2]}
18
+ # #=> {"k"=>:v, "time"=>1315493211.88372, "id"=>"2", "array"=>[0, 1, 2]}
19
+ # end
20
+ #
21
+ # p collection.all
22
+ # #=> [{"k"=>:v, "time"=>1315493211.86451, "array"=>[0, 1, 2], "id"=>"1"}
23
+ # #=> , {"k"=>:v, "time"=>1315493211.88372, "array"=>[0, 1, 2], "id"=>"2"}
24
+ # #=> ]
25
+ #
26
+ #
27
+ # ydb[:tablename].create(:foo => :bar)
28
+ #
29
+ # puts IO.read(ydb.path)
30
+ # #=> ---
31
+ # #=> tablename:
32
+ # #=> "1":
33
+ # #=> foo: :bar
34
+ # #=> id: "1"
35
+ # #=> posts:
36
+ # #=> "1":
37
+ # #=> k: :v
38
+ # #=> time: 1315493211.86451
39
+ # #=> id: "1"
40
+ # #=> array:
41
+ # #=> - 0
42
+ # #=> - 1
43
+ # #=> - 2
44
+ # #=> "2":
45
+ # #=> k: :v
46
+ # #=> time: 1315493211.88372
47
+ # #=> id: "2"
48
+ # #=> array:
49
+ # #=> - 0
50
+ # #=> - 1
51
+ # #=> - 2
52
+
53
+ require 'yaml/store'
54
+ require 'fileutils'
55
+
56
+ class YDB
57
+ Version = '0.0.1' unless defined?(Version)
58
+
59
+ def YDB.version
60
+ YDB::Version
61
+ end
62
+
63
+ def YDB.dependencies
64
+ {
65
+ 'map' => [ 'map' , '~> 4.4.0' ],
66
+ }
67
+ end
68
+
69
+ def YDB.libdir(*args, &block)
70
+ @libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
71
+ args.empty? ? @libdir : File.join(@libdir, *args)
72
+ ensure
73
+ if block
74
+ begin
75
+ $LOAD_PATH.unshift(@libdir)
76
+ block.call()
77
+ ensure
78
+ $LOAD_PATH.shift()
79
+ end
80
+ end
81
+ end
82
+
83
+ def YDB.load(*libs)
84
+ libs = libs.join(' ').scan(/[^\s+]+/)
85
+ YDB.libdir{ libs.each{|lib| Kernel.load(lib) } }
86
+ end
87
+
88
+ # gems
89
+ #
90
+ begin
91
+ require 'rubygems'
92
+ rescue LoadError
93
+ nil
94
+ end
95
+
96
+ if defined?(gem)
97
+ YDB.dependencies.each do |lib, dependency|
98
+ gem(*dependency) if defined?(gem)
99
+ require(lib)
100
+ end
101
+ end
102
+
103
+ attr_accessor :path
104
+
105
+ def initialize(*args)
106
+ options = Map.options_for!(args)
107
+ @path = ( args.shift || options[:path] || YDB.default_path ).to_s
108
+ FileUtils.mkdir_p(File.dirname(@path)) rescue nil
109
+ end
110
+
111
+ def rm_f
112
+ FileUtils.rm_f(@path) rescue nil
113
+ end
114
+
115
+ def rm_rf
116
+ FileUtils.rm_rf(@path) rescue nil
117
+ end
118
+
119
+ def truncate
120
+ rm_f
121
+ end
122
+
123
+ def ydb
124
+ self
125
+ end
126
+
127
+ def ystore
128
+ @ystore ||= YAML::Store.new(path)
129
+ end
130
+
131
+ class Collection
132
+ def initialize(name, ydb)
133
+ @name = name.to_s
134
+ @ydb = ydb
135
+ end
136
+
137
+ def save(data = {})
138
+ @ydb.save(@name, data)
139
+ end
140
+ alias_method(:create, :save)
141
+ alias_method(:update, :save)
142
+
143
+ def find(id = :all)
144
+ @ydb.find(@name, id)
145
+ end
146
+
147
+ def all
148
+ find(:all)
149
+ end
150
+
151
+ def [](id)
152
+ find(id)
153
+ end
154
+
155
+ def []=(id, data = {})
156
+ data.delete(:id)
157
+ data.delete('id')
158
+ data[:id] = id
159
+ save(data)
160
+ end
161
+
162
+ def delete(id)
163
+ @ydb.delete(@name, id)
164
+ id
165
+ end
166
+ alias_method('destroy', 'delete')
167
+
168
+ def to_hash
169
+ transaction{|y| y[@name]}
170
+ end
171
+
172
+ def size
173
+ to_hash.size
174
+ end
175
+ alias_method('count', 'size')
176
+
177
+ def to_yaml(*args, &block)
178
+ Hash.new.update(to_hash).to_yaml(*args, &block)
179
+ end
180
+
181
+ def transaction(*args, &block)
182
+ @ydb.ystore.transaction(*args, &block)
183
+ end
184
+
185
+ end
186
+
187
+ def collection(name)
188
+ Collection.new(name, ydb)
189
+ end
190
+ alias_method('[]', 'collection')
191
+
192
+ def method_missing(method, *args, &block)
193
+ if args.empty? and block.nil?
194
+ return self.collection(method)
195
+ end
196
+ super
197
+ end
198
+
199
+ def transaction(*args, &block)
200
+ ystore.transaction(*args, &block)
201
+ end
202
+
203
+ def save(collection, data)
204
+ data = data_for(data)
205
+ ystore.transaction do |y|
206
+ collection = (y[collection.to_s] ||= {})
207
+ id = next_id_for(collection, data)
208
+ collection[id] = data
209
+ record = collection[id]
210
+ id
211
+ end
212
+ end
213
+
214
+ def data_for(data)
215
+ data ? Map.for(data) : nil
216
+ end
217
+
218
+ alias_method(:create, :save)
219
+
220
+ def find(collection, id = :all, &block)
221
+ ystore.transaction do |y|
222
+ collection = (y[collection.to_s] ||= {})
223
+ if id.nil? or id == :all
224
+ list = collection.values.map{|data| data_for(data)}
225
+ if block
226
+ collection[:all] = list.map{|record| data_for(block.call(record))}
227
+ else
228
+ list
229
+ end
230
+ else
231
+ key = String(id)
232
+ record = data_for(collection[key])
233
+ if block
234
+ collection[key] = data_for(block.call(record))
235
+ else
236
+ record
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ def update(collection, id = :all, updates = {})
243
+ data = data_for(data)
244
+ find(collection, id) do |record|
245
+ record.update(updates)
246
+ end
247
+ end
248
+
249
+ def delete(collection, id = :all)
250
+ ystore.transaction do |y|
251
+ collection = (y[collection.to_s] ||= {})
252
+ if id.nil? or id == :all
253
+ collection.clear()
254
+ else
255
+ deleted = collection.delete(String(id))
256
+ data_for(deleted) if deleted
257
+ end
258
+ end
259
+ end
260
+ alias_method('destroy', 'delete')
261
+
262
+ def next_id_for(collection, data)
263
+ data = data_for(data)
264
+ begin
265
+ id = id_for(data)
266
+ raise if id.strip.empty?
267
+ id
268
+ rescue
269
+ data['id'] = String(collection.size + 1)
270
+ id_for(data)
271
+ end
272
+ end
273
+
274
+ def id_for(data)
275
+ data = data_for(data)
276
+ %w( id _id ).each{|key| return String(data[key]) if data.has_key?(key)}
277
+ raise("no id discoverable for #{ data.inspect }")
278
+ end
279
+
280
+ def to_hash
281
+ ystore.transaction do |y|
282
+ y.roots.inject(Hash.new){|h,k| h.update(k => y[k])}
283
+ end
284
+ end
285
+
286
+ def to_yaml(*args, &block)
287
+ to_hash.to_yaml(*args, &block)
288
+ end
289
+
290
+ class << YDB
291
+ attr_writer :root
292
+ attr_writer :instance
293
+
294
+ def default_root()
295
+ defined?(Rails.root) && Rails.root ? File.join(Rails.root.to_s, 'db') : '.'
296
+ end
297
+
298
+ def default_path()
299
+ File.join(default_root, 'ydb.yml')
300
+ end
301
+
302
+ def method_missing(method, *args, &block)
303
+ super unless instance.respond_to?(method)
304
+ instance.send(method, *args, &block)
305
+ end
306
+
307
+ def instance
308
+ @instance ||= YDB.new(YDB.default_path)
309
+ end
310
+
311
+ def root
312
+ @root ||= default_root
313
+ end
314
+
315
+ def tmp(&block)
316
+ require 'tempfile' unless defined?(Tempfile)
317
+ tempfile = Tempfile.new("#{ Process.pid }-#{ Process.ppid }-#{ Time.now.to_f }-#{ rand }")
318
+ path = tempfile.path
319
+ ydb = new(:path => path)
320
+ if block
321
+ begin
322
+ block.call(ydb)
323
+ ensure
324
+ ydb.rm_rf
325
+ end
326
+ else
327
+ ydb
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ Ydb = YDb = YDB
@@ -0,0 +1,30 @@
1
+ ## ydb.gemspec
2
+ #
3
+
4
+ Gem::Specification::new do |spec|
5
+ spec.name = "ydb"
6
+ spec.version = "0.0.1"
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.summary = "ydb"
9
+ spec.description = "description: ydb kicks the ass"
10
+
11
+ spec.files =
12
+ ["README", "Rakefile", "lib", "lib/ydb.rb", "ydb.gemspec"]
13
+
14
+ spec.executables = []
15
+
16
+ spec.require_path = "lib"
17
+
18
+ spec.test_files = nil
19
+
20
+
21
+ spec.add_dependency(*["map", "~> 4.4.0"])
22
+
23
+
24
+ spec.extensions.push(*[])
25
+
26
+ spec.rubyforge_project = "codeforpeople"
27
+ spec.author = "Ara T. Howard"
28
+ spec.email = "ara.t.howard@gmail.com"
29
+ spec.homepage = "https://github.com/ahoward/ydb"
30
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ydb
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Ara T. Howard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-08 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: map
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 47
30
+ segments:
31
+ - 4
32
+ - 4
33
+ - 0
34
+ version: 4.4.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: "description: ydb kicks the ass"
38
+ email: ara.t.howard@gmail.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - README
47
+ - Rakefile
48
+ - lib/ydb.rb
49
+ - ydb.gemspec
50
+ has_rdoc: true
51
+ homepage: https://github.com/ahoward/ydb
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project: codeforpeople
80
+ rubygems_version: 1.4.2
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: ydb
84
+ test_files: []
85
+