ydb 0.0.1

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