bike 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.rdoc +124 -0
- data/bin/bike +35 -0
- data/lib/_error.rb +14 -0
- data/lib/_field.rb +260 -0
- data/lib/_i18n.rb +144 -0
- data/lib/_parser.rb +256 -0
- data/lib/_path.rb +86 -0
- data/lib/_storage/_storage.rb +215 -0
- data/lib/_storage/file.rb +201 -0
- data/lib/_storage/sequel.rb +174 -0
- data/lib/_storage/temp.rb +73 -0
- data/lib/_widget/action_create.rb +23 -0
- data/lib/_widget/action_login.rb +22 -0
- data/lib/_widget/action_signup.rb +16 -0
- data/lib/_widget/action_update.rb +16 -0
- data/lib/_widget/crumb.rb +24 -0
- data/lib/_widget/done.rb +16 -0
- data/lib/_widget/login.rb +25 -0
- data/lib/_widget/me.rb +31 -0
- data/lib/_widget/message.rb +51 -0
- data/lib/_widget/navi.rb +88 -0
- data/lib/_widget/submit.rb +49 -0
- data/lib/_widget/view_ym.rb +77 -0
- data/lib/_workflow/_workflow.rb +89 -0
- data/lib/_workflow/attachment.rb +50 -0
- data/lib/_workflow/blog.rb +28 -0
- data/lib/_workflow/contact.rb +23 -0
- data/lib/_workflow/forum.rb +26 -0
- data/lib/_workflow/register.rb +39 -0
- data/lib/bike.rb +396 -0
- data/lib/meta/_meta.rb +20 -0
- data/lib/meta/group.rb +19 -0
- data/lib/meta/id.rb +59 -0
- data/lib/meta/owner.rb +21 -0
- data/lib/meta/timestamp.rb +118 -0
- data/lib/scalar/checkbox.rb +68 -0
- data/lib/scalar/file.rb +144 -0
- data/lib/scalar/img.rb +112 -0
- data/lib/scalar/password.rb +58 -0
- data/lib/scalar/radio.rb +47 -0
- data/lib/scalar/select.rb +47 -0
- data/lib/scalar/text.rb +38 -0
- data/lib/scalar/textarea.rb +35 -0
- data/lib/scalar/textarea_pre.rb +14 -0
- data/lib/scalar/textarea_wiki.rb +173 -0
- data/lib/set/_set.rb +196 -0
- data/lib/set/dynamic.rb +177 -0
- data/lib/set/static.rb +102 -0
- data/lib/set/static_folder.rb +96 -0
- data/locale/en/index.po +242 -0
- data/locale/index.pot +243 -0
- data/locale/ja/index.po +242 -0
- data/locale/lazy_parser.rb +54 -0
- data/skel/config.ru +27 -0
- data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
- data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
- data/skel/skin/_users/00000000_frank.yaml +12 -0
- data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
- data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
- data/skel/skin/_users/00000000_root.yaml +11 -0
- data/skel/skin/_users/css/users.css +21 -0
- data/skel/skin/_users/css/users.less +25 -0
- data/skel/skin/_users/done.html +42 -0
- data/skel/skin/_users/index.html +46 -0
- data/skel/skin/_users/index.yaml +3 -0
- data/skel/skin/_users/summary.html +40 -0
- data/skel/skin/css/base.css +93 -0
- data/skel/skin/css/base.less +139 -0
- data/skel/skin/css/coax.css +199 -0
- data/skel/skin/css/coax.less +244 -0
- data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
- data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
- data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
- data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
- data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
- data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
- data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
- data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
- data/skel/skin/examples/blog/frank/index.yaml +4 -0
- data/skel/skin/examples/blog/index.html +51 -0
- data/skel/skin/examples/blog/rss.xml +18 -0
- data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
- data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
- data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
- data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
- data/skel/skin/examples/contact/index.html +47 -0
- data/skel/skin/examples/contact/js/contact.js +13 -0
- data/skel/skin/examples/contact/summary.html +54 -0
- data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
- data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
- data/skel/skin/examples/forum/index.html +68 -0
- data/skel/skin/examples/forum/summary.html +47 -0
- data/skel/skin/examples/index.html +73 -0
- data/skel/skin/index.html +39 -0
- data/skel/skin/js/base.js +50 -0
- data/t/locale/de/index.po +19 -0
- data/t/locale/en-GB/index.po +25 -0
- data/t/locale/ja/index.po +30 -0
- data/t/skin/_users/00000000_test.yaml +3 -0
- data/t/skin/_users/index.html +13 -0
- data/t/skin/foo/20091120_0001.yaml +7 -0
- data/t/skin/foo/bar/20091120_0001.yaml +5 -0
- data/t/skin/foo/bar/index.yaml +5 -0
- data/t/skin/foo/baz/css/baz.css +1 -0
- data/t/skin/foo/css/foo.css +1 -0
- data/t/skin/foo/index.html +14 -0
- data/t/skin/foo/index.yaml +7 -0
- data/t/skin/foo/not_css/foo.css +1 -0
- data/t/skin/foo/qux/index.html +8 -0
- data/t/skin/foo/qux/moo/index.html +6 -0
- data/t/skin/foo/sub-20100306_0001.yaml +3 -0
- data/t/skin/index.yaml +3 -0
- data/t/skin/t_attachment/index.html +13 -0
- data/t/skin/t_contact/done.html +6 -0
- data/t/skin/t_contact/index.html +9 -0
- data/t/skin/t_file/index.html +16 -0
- data/t/skin/t_img/index.html +14 -0
- data/t/skin/t_img/test.jpg +0 -0
- data/t/skin/t_select/index.html +9 -0
- data/t/skin/t_store/index.html +9 -0
- data/t/skin/t_summary/20100326_0001.yaml +3 -0
- data/t/skin/t_summary/create.html +9 -0
- data/t/skin/t_summary/index.html +9 -0
- data/t/skin/t_summary/summary.html +9 -0
- data/t/t.rb +27 -0
- data/t/test_bike.rb +768 -0
- data/t/test_call.rb +1281 -0
- data/t/test_checkbox.rb +273 -0
- data/t/test_field.rb +330 -0
- data/t/test_file.rb +900 -0
- data/t/test_i18n.rb +325 -0
- data/t/test_id.rb +215 -0
- data/t/test_img.rb +328 -0
- data/t/test_meta.rb +57 -0
- data/t/test_parser.rb +1516 -0
- data/t/test_password.rb +188 -0
- data/t/test_radio.rb +226 -0
- data/t/test_role.rb +249 -0
- data/t/test_select.rb +182 -0
- data/t/test_set_complex.rb +527 -0
- data/t/test_set_dynamic.rb +1504 -0
- data/t/test_set_folder.rb +515 -0
- data/t/test_set_permit.rb +246 -0
- data/t/test_set_static.rb +468 -0
- data/t/test_storage.rb +915 -0
- data/t/test_text.rb +125 -0
- data/t/test_textarea.rb +138 -0
- data/t/test_timestamp.rb +473 -0
- data/t/test_workflow.rb +367 -0
- metadata +347 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Author:: Akira FUNAI
|
4
|
+
# Copyright:: Copyright (c) 2009 Akira FUNAI
|
5
|
+
|
6
|
+
class Bike::Storage
|
7
|
+
|
8
|
+
def self.instance(sd)
|
9
|
+
if folder = sd[:folder]
|
10
|
+
if folder != sd[:parent]
|
11
|
+
Temp.new sd
|
12
|
+
else
|
13
|
+
klass = Bike['storage']['default'].capitalize
|
14
|
+
self.const_get(klass).new sd
|
15
|
+
end
|
16
|
+
else
|
17
|
+
Temp.new sd
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.available?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :sd
|
26
|
+
|
27
|
+
def initialize(sd)
|
28
|
+
@sd = sd
|
29
|
+
end
|
30
|
+
|
31
|
+
def persistent?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def order
|
36
|
+
@sd[:order]
|
37
|
+
end
|
38
|
+
|
39
|
+
def select(conds = {})
|
40
|
+
_cast conds
|
41
|
+
item_ids = _select(conds)
|
42
|
+
item_ids = _sort(item_ids, conds)
|
43
|
+
item_ids = _page(item_ids, conds)
|
44
|
+
((conds[:order] || order) =~ /^-/) ? item_ids.reverse : item_ids
|
45
|
+
end
|
46
|
+
|
47
|
+
def build(v)
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def store(id, v)
|
56
|
+
id
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete(id)
|
60
|
+
id
|
61
|
+
end
|
62
|
+
|
63
|
+
def navi(conds)
|
64
|
+
conds[:p] = '1' unless conds[:p] || conds[:id] || (@sd[:p_size].to_i < 1)
|
65
|
+
navi = {}
|
66
|
+
(([:id, :p, :d] & conds.keys) | conds.keys).each {|cid|
|
67
|
+
next unless conds[cid] && respond_to?("_sibs_#{cid}", true)
|
68
|
+
sibs = __send__("_sibs_#{cid}", conds)
|
69
|
+
|
70
|
+
c = (c.is_a?(::Array) && c.size < 2) ? conds[cid].first : conds[cid]
|
71
|
+
if i = sibs.index(c)
|
72
|
+
if !navi[:prev] && i > 0
|
73
|
+
navi[:prev] = conds.merge(cid => sibs[i - 1])
|
74
|
+
if ![:id, :p].include? cid
|
75
|
+
navi[:prev][:id] = _select_without(:id, navi[:prev]).last if conds[:id]
|
76
|
+
navi[:prev][:p] = _p_count(navi[:prev]).to_s if conds[:p]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if !navi[:next] && i < (sibs.size - 1)
|
80
|
+
navi[:next] = conds.merge(cid => sibs[i + 1])
|
81
|
+
if ![:id, :p].include? cid
|
82
|
+
navi[:next][:id] = _select_without(:id, navi[:next]).first if conds[:id]
|
83
|
+
navi[:next][:p] = '1' if conds[:p]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
navi[:sibs] ||= {cid => sibs} if navi[:prev] || navi[:next]
|
88
|
+
|
89
|
+
break if navi[:prev] && navi[:next]
|
90
|
+
}
|
91
|
+
|
92
|
+
navi
|
93
|
+
end
|
94
|
+
|
95
|
+
def last(cid, conds)
|
96
|
+
__send__("_sibs_#{cid}", conds).last
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def _cast(conds)
|
102
|
+
([:d, :id, :p] & conds.keys).each {|cid|
|
103
|
+
case cid
|
104
|
+
when :d
|
105
|
+
conds[:d] = conds[:d].first if conds[:d].is_a? ::Array
|
106
|
+
conds[:d] = conds[:d].to_s
|
107
|
+
conds[:d] = last(:d, conds) if conds[:d] =~ /9999(99)?(99)?/
|
108
|
+
conds[:d] = nil unless conds[:d] =~ Bike::REX::COND_D
|
109
|
+
when :id
|
110
|
+
conds[:id] = Array(conds[:id]).collect {|id|
|
111
|
+
case id
|
112
|
+
when '99999999_9999', 'last'
|
113
|
+
last(:id, conds)
|
114
|
+
when /\A#{Bike::REX::ID_SHORT}\z/
|
115
|
+
"00000000_#{id}"
|
116
|
+
when Bike::REX::ID, Bike::REX::ID_NEW
|
117
|
+
id
|
118
|
+
end
|
119
|
+
}.uniq.compact
|
120
|
+
when :p
|
121
|
+
conds[:p] = conds[:p].first if conds[:p].is_a? ::Array
|
122
|
+
conds[:p] = conds[:p].to_s
|
123
|
+
conds[:p] = last(:p, conds) if conds[:p] == 'last'
|
124
|
+
conds[:p] = nil unless conds[:p] =~ /^\d+$/
|
125
|
+
end
|
126
|
+
}
|
127
|
+
conds
|
128
|
+
end
|
129
|
+
|
130
|
+
def _select(conds)
|
131
|
+
if conds[:id]
|
132
|
+
_select_by_id(conds) | (@sd.instance_variable_get(:@item_object).keys & Array(conds[:id]))
|
133
|
+
elsif cid = (conds.keys - [:order, :p]).first
|
134
|
+
m = "_select_by_#{cid}"
|
135
|
+
respond_to?(m, true) ? __send__(m, conds) : []
|
136
|
+
else
|
137
|
+
_select_all(conds) | @sd.instance_variable_get(:@item_object).keys
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def _sort(item_ids, conds)
|
142
|
+
item_ids.sort
|
143
|
+
end
|
144
|
+
|
145
|
+
def _page(item_ids, conds)
|
146
|
+
size = @sd[:p_size].to_i
|
147
|
+
return item_ids if size < 1
|
148
|
+
|
149
|
+
page = conds[:p].to_i
|
150
|
+
page = 1 if page < 1
|
151
|
+
Array item_ids[(page - 1) * size, size]
|
152
|
+
end
|
153
|
+
|
154
|
+
def _sibs_id(conds)
|
155
|
+
_select_without(:id, conds)
|
156
|
+
end
|
157
|
+
|
158
|
+
def _sibs_p(conds)
|
159
|
+
p_count = _p_count(conds)
|
160
|
+
p_count ? (1..p_count).collect {|i| i.to_s } : []
|
161
|
+
end
|
162
|
+
|
163
|
+
def _sibs_d(conds)
|
164
|
+
rex_d = /^\d{#{conds[:d].length}}/
|
165
|
+
_select_without(:id, :p, :d, conds).collect {|id| id[rex_d] }.uniq.compact
|
166
|
+
end
|
167
|
+
|
168
|
+
def _select_without(*cids)
|
169
|
+
conds = cids.pop.dup
|
170
|
+
cids.each {|cid| conds.delete cid }
|
171
|
+
_sort(_select(conds), conds)
|
172
|
+
end
|
173
|
+
|
174
|
+
def _p_count(conds)
|
175
|
+
(_select_without(:id, :p, conds).size / @sd[:p_size].to_f).ceil unless @sd[:p_size].to_f == 0
|
176
|
+
end
|
177
|
+
|
178
|
+
def cast_ids(ids)
|
179
|
+
Array(ids).collect {|i|
|
180
|
+
id = (i =~ /^[a-z]/) ? "00000000_#{i}" : i
|
181
|
+
id if id =~ Bike::REX::ID
|
182
|
+
}.compact
|
183
|
+
end
|
184
|
+
|
185
|
+
def new_id(v = {})
|
186
|
+
return "00000000_#{v['_id']}" if v.is_a?(::Hash) && v['_id'] =~ /\A#{Bike::REX::ID_SHORT}\z/
|
187
|
+
|
188
|
+
if v.is_a?(::Hash) && v['_timestamp'] && v['_timestamp']['published'].is_a?(::Time)
|
189
|
+
d = v['_timestamp']['published'].strftime '%Y%m%d'
|
190
|
+
else
|
191
|
+
d = Time.now.strftime '%Y%m%d'
|
192
|
+
end
|
193
|
+
if max_in_d = _select(:d => d).sort.last
|
194
|
+
max_in_d.succ
|
195
|
+
else
|
196
|
+
d + '_0001'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def new_id?(id, v)
|
201
|
+
if id == :new_id
|
202
|
+
true
|
203
|
+
elsif !v.is_a?(::Hash)
|
204
|
+
false
|
205
|
+
elsif id =~ /_#{Bike::REX::ID_SHORT}\z/
|
206
|
+
v['_id'] =~ /\A#{Bike::REX::ID_SHORT}\z/ &&
|
207
|
+
id != new_id(v)
|
208
|
+
else
|
209
|
+
v['_timestamp'] &&
|
210
|
+
v['_timestamp']['published'].is_a?(::Time) &&
|
211
|
+
id !~ /\A#{v['_timestamp']['published'].strftime('%Y%m%d')}/
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Author:: Akira FUNAI
|
4
|
+
# Copyright:: Copyright (c) 2009 Akira FUNAI
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'yaml'
|
8
|
+
require 'ya2yaml'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
class Bike::Storage::File < Bike::Storage
|
12
|
+
|
13
|
+
def self.traverse(dir = '/', root = Bike['storage']['File']['data_dir'], &block)
|
14
|
+
::Dir.glob(::File.join(root, dir, '*')).sort.collect {|file|
|
15
|
+
ftype = ::File.ftype file
|
16
|
+
base_name = ::File.basename file
|
17
|
+
id, ext = base_name.split('.', 2)
|
18
|
+
id = "main-#{id}" if id =~ Bike::REX::ID
|
19
|
+
full_name = ::File.join(dir, id).gsub(::File::SEPARATOR, '-')
|
20
|
+
|
21
|
+
if ftype == 'file' && id.sub(/^([^\d\-]+-)+/, '') =~ Bike::REX::ID
|
22
|
+
val = nil
|
23
|
+
::File.open(file, 'r') {|f|
|
24
|
+
f.flock ::File::LOCK_SH
|
25
|
+
f.binmode
|
26
|
+
val = f.read
|
27
|
+
f.flock ::File::LOCK_UN
|
28
|
+
}
|
29
|
+
block.call(
|
30
|
+
:dir => dir,
|
31
|
+
:base_name => base_name,
|
32
|
+
:full_name => full_name,
|
33
|
+
:ext => ext,
|
34
|
+
:val => (ext == 'yaml' ? YAML.load(val) : val)
|
35
|
+
)
|
36
|
+
elsif ftype == 'directory' && base_name !~ /\A#{Bike::REX::DIR_STATIC}\z/
|
37
|
+
self.traverse(::File.join(dir, base_name), root, &block)
|
38
|
+
end
|
39
|
+
}.compact.flatten
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.load_skel
|
43
|
+
self.traverse('/', Bike['skin_dir']) {|entry|
|
44
|
+
dir = ::File.join(Bike['storage']['File']['data_dir'], entry[:dir])
|
45
|
+
unless ::File.exists? ::File.join(dir, entry[:base_name])
|
46
|
+
::FileUtils.mkpath(dir) unless ::File.directory? dir
|
47
|
+
::FileUtils.cp(
|
48
|
+
::File.join(Bike['skin_dir'], entry[:dir], entry[:base_name]),
|
49
|
+
::File.join(dir, entry[:base_name]),
|
50
|
+
{:preserve => true}
|
51
|
+
)
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.available?
|
57
|
+
Bike['storage']['File'] && Bike['storage']['File']['data_dir']
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(sd)
|
61
|
+
super
|
62
|
+
unless @@loaded ||= false
|
63
|
+
entries = ::Dir.glob ::File.join(Bike['storage']['File']['data_dir'], '*')
|
64
|
+
self.class.load_skel if entries.empty?
|
65
|
+
@@loaded = true
|
66
|
+
end
|
67
|
+
@dir = ::File.join(Bike['storage']['File']['data_dir'], @sd[:folder][:dir])
|
68
|
+
::FileUtils.mkpath(@dir) unless ::File.directory? @dir
|
69
|
+
end
|
70
|
+
|
71
|
+
def val(id = nil)
|
72
|
+
if id
|
73
|
+
load id
|
74
|
+
else
|
75
|
+
# this could be HUGE.
|
76
|
+
_select_all({}).inject({}) {|v, id|
|
77
|
+
v[id] = load id
|
78
|
+
v
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def build(v)
|
84
|
+
clear
|
85
|
+
v.each {|id, v| store(id, v) }
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def clear
|
90
|
+
glob.each {|file| ::File.unlink ::File.join(@dir, file) }
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def store(id, v, ext = nil)
|
95
|
+
save(id, v, ext)
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete(id)
|
99
|
+
remove_file(id) && id
|
100
|
+
end
|
101
|
+
|
102
|
+
def move(old_id, new_id)
|
103
|
+
rename_file(old_id, new_id) && new_id
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def _select_by_id(conds)
|
109
|
+
glob(Array(conds[:id])).collect {|f| f[/\d.*/][Bike::REX::ID] }.compact
|
110
|
+
end
|
111
|
+
|
112
|
+
def _select_by_d(conds)
|
113
|
+
glob(conds[:d].to_s).collect {|f| f[/\.yaml$/] && f[/\d.*/][Bike::REX::ID] }.compact
|
114
|
+
end
|
115
|
+
|
116
|
+
def _select_all(conds)
|
117
|
+
glob.collect {|f| f[/\.yaml$/] && f[/\d.*/][Bike::REX::ID] }.compact
|
118
|
+
end
|
119
|
+
|
120
|
+
def glob(id = :all)
|
121
|
+
glob_pattern = "#{file_prefix}#{pattern_for id}.[a-z]*"
|
122
|
+
::Dir.chdir(@dir) { ::Dir.glob glob_pattern }
|
123
|
+
end
|
124
|
+
|
125
|
+
def file_prefix
|
126
|
+
(@sd[:name] == 'main') ? '' : @sd[:name].sub(/^main-/, '') + '-'
|
127
|
+
end
|
128
|
+
|
129
|
+
def pattern_for(id)
|
130
|
+
if id.is_a? Array
|
131
|
+
"{#{id.join ','}}"
|
132
|
+
elsif id == :all
|
133
|
+
'[0-9]*_*'
|
134
|
+
elsif id =~ /\A\d{4,8}\z/
|
135
|
+
"#{id}*"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def load(id)
|
140
|
+
v = nil
|
141
|
+
file = glob(Array(id)).sort.first
|
142
|
+
::File.open(::File.join(@dir, file), 'r') {|f|
|
143
|
+
f.flock ::File::LOCK_SH
|
144
|
+
f.binmode
|
145
|
+
v = f.read
|
146
|
+
f.flock ::File::LOCK_UN
|
147
|
+
} if file
|
148
|
+
(file[/\.yaml$/] ? YAML.load(v) : v) if v
|
149
|
+
end
|
150
|
+
|
151
|
+
def save(id, v, ext)
|
152
|
+
if new_id?(id, v)
|
153
|
+
old_id = id
|
154
|
+
id = new_id v
|
155
|
+
end
|
156
|
+
|
157
|
+
if ext
|
158
|
+
val = v
|
159
|
+
ext = 'y' if ext == 'yaml'
|
160
|
+
remove_file id
|
161
|
+
else
|
162
|
+
val = v.ya2yaml(:syck_compatible => true)
|
163
|
+
ext = 'yaml'
|
164
|
+
end
|
165
|
+
|
166
|
+
file = "#{file_prefix}#{id}.#{ext}"
|
167
|
+
if old_id && f = ::File.open(::File.join(@dir, file), 'a')
|
168
|
+
f.seek(0, IO::SEEK_END)
|
169
|
+
return if f.pos != 0 # duplicate id
|
170
|
+
move(old_id, id) unless old_id == :new_id
|
171
|
+
end
|
172
|
+
::File.open(::File.join(@dir, file), 'a') {|f|
|
173
|
+
f.flock ::File::LOCK_EX
|
174
|
+
f.binmode
|
175
|
+
f.seek 0
|
176
|
+
f.truncate 0
|
177
|
+
f << val
|
178
|
+
f.flock ::File::LOCK_UN
|
179
|
+
f.chmod 0664
|
180
|
+
} && id
|
181
|
+
end
|
182
|
+
|
183
|
+
def remove_file(id)
|
184
|
+
glob_pattern = "#{file_prefix}#{pattern_for Array(id)}[.-]*"
|
185
|
+
files = ::Dir.chdir(@dir) { ::Dir.glob glob_pattern } # may include child files
|
186
|
+
files.each {|file|
|
187
|
+
::File.unlink ::File.join(@dir, file)
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
def rename_file(old_id, new_id)
|
192
|
+
glob_pattern = "#{file_prefix}#{pattern_for Array(old_id)}*"
|
193
|
+
files = ::Dir.chdir(@dir) { ::Dir.glob glob_pattern } # may include child files
|
194
|
+
rex = /^\A#{file_prefix}#{old_id}/
|
195
|
+
files.each {|file|
|
196
|
+
to_file = file.sub(rex, "#{file_prefix}#{new_id}")
|
197
|
+
::File.rename(::File.join(@dir, file), ::File.join(@dir, to_file))
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Author:: Akira FUNAI
|
4
|
+
# Copyright:: Copyright (c) 2009-2010 Akira FUNAI
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'sequel'
|
8
|
+
require 'yaml'
|
9
|
+
require 'ya2yaml'
|
10
|
+
|
11
|
+
class Bike::Storage::Sequel < Bike::Storage
|
12
|
+
|
13
|
+
def self.db
|
14
|
+
if Bike['storage']['Sequel'] && Bike['storage']['Sequel']['uri']
|
15
|
+
@db ||= ::Sequel.connect Bike['storage']['Sequel']['uri']
|
16
|
+
self.load_skel unless @db.table_exists? :bike_main
|
17
|
+
end
|
18
|
+
@db
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.load_skel
|
22
|
+
@db.create_table(:bike_main) {
|
23
|
+
String :full_name
|
24
|
+
String :ext
|
25
|
+
String :owner
|
26
|
+
String :body
|
27
|
+
File :binary_body
|
28
|
+
primary_key :full_name
|
29
|
+
} unless @db.table_exists? :bike_main
|
30
|
+
Bike::Storage::File.traverse('/', Bike['skin_dir']) {|entry|
|
31
|
+
@db[:bike_main].insert(
|
32
|
+
:full_name => entry[:full_name],
|
33
|
+
:ext => entry[:ext],
|
34
|
+
:owner => entry[:val]['_owner'],
|
35
|
+
:body => entry[:val].ya2yaml(:syck_compatible => true),
|
36
|
+
:binary_body => (entry[:ext] == 'yaml') ? nil : entry[:val].to_sequel_blob
|
37
|
+
) unless @db[:bike_main][:full_name => entry[:full_name]]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.available?
|
42
|
+
self.db
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(sd)
|
46
|
+
super
|
47
|
+
@dataset = Bike::Storage::Sequel.db[:bike_main]
|
48
|
+
@dirname = @sd[:full_name]
|
49
|
+
end
|
50
|
+
|
51
|
+
def val(id = nil)
|
52
|
+
if id
|
53
|
+
load id
|
54
|
+
else
|
55
|
+
# this could be HUGE.
|
56
|
+
_select_all({}).inject({}) {|v, id|
|
57
|
+
v[id] = load id
|
58
|
+
v
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def build(v)
|
64
|
+
clear
|
65
|
+
v.each {|id, v| store(id, v) } if v
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def clear
|
70
|
+
@dataset.grep(:full_name, _full_name('%')).delete
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def store(id, v, ext = nil)
|
75
|
+
save(id, v, ext)
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(id)
|
79
|
+
@dataset.filter(_conds(id)).delete &&
|
80
|
+
@dataset.grep(:full_name, _full_name("#{id}-%")).and(~:ext => 'yaml').delete &&
|
81
|
+
id
|
82
|
+
end
|
83
|
+
|
84
|
+
def move(old_id, new_id)
|
85
|
+
rename(old_id, new_id) && new_id
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def _select_by_id(conds)
|
91
|
+
@dataset.filter(_conds(conds[:id])).collect {|v| _id v[:full_name] }
|
92
|
+
end
|
93
|
+
|
94
|
+
def _select_by_d(conds)
|
95
|
+
@dataset.
|
96
|
+
grep(:full_name, _full_name("#{conds[:d]}%")).
|
97
|
+
and(:ext => 'yaml').
|
98
|
+
collect {|v| _id v[:full_name] }
|
99
|
+
end
|
100
|
+
|
101
|
+
def _select_all(conds)
|
102
|
+
@dataset.
|
103
|
+
grep(:full_name, _full_name('%')).
|
104
|
+
and(:ext => 'yaml').
|
105
|
+
collect {|v| _id v[:full_name] }
|
106
|
+
end
|
107
|
+
|
108
|
+
# TODO: overrride _sort() & _page() (they can be done in _select_*)
|
109
|
+
|
110
|
+
def _conds(id)
|
111
|
+
(id == :all) ? {} : {:full_name => _full_name(id)}
|
112
|
+
end
|
113
|
+
|
114
|
+
def _full_name(id)
|
115
|
+
"#{@dirname}-#{id.is_a?(::Array) ? id.first : id}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def _id(full_name)
|
119
|
+
full_name.sub("#{@dirname}-", '')
|
120
|
+
end
|
121
|
+
|
122
|
+
def load(id)
|
123
|
+
# TODO: cache in _select_*
|
124
|
+
v = @dataset[:full_name => _full_name(id)]
|
125
|
+
(v[:ext] == 'yaml' ? YAML.load(v[:body]) : v[:binary_body]) if v
|
126
|
+
end
|
127
|
+
|
128
|
+
def save(id, v, ext)
|
129
|
+
Bike::Storage::Sequel.db.transaction {
|
130
|
+
if new_id?(id, v)
|
131
|
+
old_id = id
|
132
|
+
id = new_id v
|
133
|
+
end
|
134
|
+
|
135
|
+
full_name = _full_name id
|
136
|
+
|
137
|
+
if ext
|
138
|
+
ext = 'y' if ext == 'yaml'
|
139
|
+
val = {
|
140
|
+
:full_name => full_name,
|
141
|
+
:ext => ext,
|
142
|
+
:binary_body => v.to_sequel_blob,
|
143
|
+
}
|
144
|
+
else
|
145
|
+
val = {
|
146
|
+
:full_name => full_name,
|
147
|
+
:ext => 'yaml',
|
148
|
+
:owner => v['_owner'],
|
149
|
+
:body => v.ya2yaml(:syck_compatible => true),
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
if old_id
|
154
|
+
return if @dataset[:full_name => full_name] # duplicate id
|
155
|
+
move(old_id, id) unless old_id == :new_id
|
156
|
+
end
|
157
|
+
if @dataset[:full_name => full_name]
|
158
|
+
@dataset[:full_name => full_name] = val
|
159
|
+
else
|
160
|
+
@dataset.insert val
|
161
|
+
end
|
162
|
+
id
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
def rename(old_id, new_id)
|
167
|
+
@dataset.grep(:full_name, _full_name("#{old_id}%")).each {|v|
|
168
|
+
@dataset[:full_name => v[:full_name]] = v.merge(
|
169
|
+
:full_name => v[:full_name].sub(_full_name(old_id), _full_name(new_id))
|
170
|
+
)
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|