bike 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +124 -0
  3. data/bin/bike +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +144 -0
  7. data/lib/_parser.rb +256 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +215 -0
  10. data/lib/_storage/file.rb +201 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/bike.rb +396 -0
  32. data/lib/meta/_meta.rb +20 -0
  33. data/lib/meta/group.rb +19 -0
  34. data/lib/meta/id.rb +59 -0
  35. data/lib/meta/owner.rb +21 -0
  36. data/lib/meta/timestamp.rb +118 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +196 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +73 -0
  95. data/skel/skin/index.html +39 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/qux/index.html +8 -0
  111. data/t/skin/foo/qux/moo/index.html +6 -0
  112. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  113. data/t/skin/index.yaml +3 -0
  114. data/t/skin/t_attachment/index.html +13 -0
  115. data/t/skin/t_contact/done.html +6 -0
  116. data/t/skin/t_contact/index.html +9 -0
  117. data/t/skin/t_file/index.html +16 -0
  118. data/t/skin/t_img/index.html +14 -0
  119. data/t/skin/t_img/test.jpg +0 -0
  120. data/t/skin/t_select/index.html +9 -0
  121. data/t/skin/t_store/index.html +9 -0
  122. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  123. data/t/skin/t_summary/create.html +9 -0
  124. data/t/skin/t_summary/index.html +9 -0
  125. data/t/skin/t_summary/summary.html +9 -0
  126. data/t/t.rb +27 -0
  127. data/t/test_bike.rb +768 -0
  128. data/t/test_call.rb +1281 -0
  129. data/t/test_checkbox.rb +273 -0
  130. data/t/test_field.rb +330 -0
  131. data/t/test_file.rb +900 -0
  132. data/t/test_i18n.rb +325 -0
  133. data/t/test_id.rb +215 -0
  134. data/t/test_img.rb +328 -0
  135. data/t/test_meta.rb +57 -0
  136. data/t/test_parser.rb +1516 -0
  137. data/t/test_password.rb +188 -0
  138. data/t/test_radio.rb +226 -0
  139. data/t/test_role.rb +249 -0
  140. data/t/test_select.rb +182 -0
  141. data/t/test_set_complex.rb +527 -0
  142. data/t/test_set_dynamic.rb +1504 -0
  143. data/t/test_set_folder.rb +515 -0
  144. data/t/test_set_permit.rb +246 -0
  145. data/t/test_set_static.rb +468 -0
  146. data/t/test_storage.rb +915 -0
  147. data/t/test_text.rb +125 -0
  148. data/t/test_textarea.rb +138 -0
  149. data/t/test_timestamp.rb +473 -0
  150. data/t/test_workflow.rb +367 -0
  151. metadata +347 -0
@@ -0,0 +1,144 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Bike::I18n
7
+
8
+ class Msgstr < String
9
+ def %(*args)
10
+ if args.first.is_a? ::Hash
11
+ self.gsub(/%\{(\w+)\}/) { args.first[$1.intern].to_s }
12
+ else
13
+ args = args.first if args.first.is_a? ::Array
14
+ ::String.new(self.gsub(/%\{(\w+)\}/, '%s')) % args
15
+ end
16
+ end
17
+ end
18
+
19
+ module REX
20
+ COMMENT = %r{^\s*\#}
21
+ COMMENT_FUZZY = %r{^\s*\#\,\s*fuzzy}
22
+ MSGID = %r{^\s*msgid\s*"(.*?[^\\])"}
23
+ MSGSTR = %r{^\s*msgstr\s*"(.*?[^\\])"}
24
+ MSGSTR_PLURAL = %r{^\s*msgstr\[(\d+)\]\s*"(.*?[^\\])"}
25
+ PLURAL_EXPRESSION = %r{
26
+ ^"Plural-Forms:.*plural=
27
+ ((?:
28
+ n(?=\s*(?:[\+\-\%]|==|!=|>|<|>=|<=))|
29
+ [\d\s\+\-\%\(\)\?\:]+|
30
+ ==|!=|>|<|>=|<=|&&|\|\|)+).*"
31
+ }xi
32
+ end
33
+
34
+ def self.lang
35
+ Thread.current[:lang] || []
36
+ end
37
+
38
+ def self.lang=(http_accept_language)
39
+ Thread.current[:msg] = nil
40
+ tokens = http_accept_language.to_s.split(/,/)
41
+ Thread.current[:lang] = tokens.sort_by {|t| # rfc3282
42
+ [
43
+ (t =~ /q=([\d\.]+)/) ? $1.to_f : 1.0,
44
+ -tokens.index(t)
45
+ ]
46
+ }.reverse.collect {|i|
47
+ if i =~ /([a-z]{1,8})(-[a-z]{1,8})?/i # rfc2616
48
+ $2 ? ($1.downcase + $2.upcase) : $1.downcase
49
+ end
50
+ }
51
+ end
52
+
53
+ def self.domain
54
+ @@domain ||= 'index'
55
+ end
56
+
57
+ def self.domain=(domain)
58
+ @@domain = domain
59
+ end
60
+
61
+ def self.po_dir
62
+ @@po_dir ||= ::File.expand_path('../locale', ::File.dirname(__FILE__))
63
+ end
64
+
65
+ def self.po_dir=(po_dir)
66
+ @@po_dir = po_dir
67
+ end
68
+
69
+ def self.bindtextdomain(domain, po_dir)
70
+ self.domain = domain
71
+ self.po_dir = po_dir
72
+ end
73
+
74
+ def self.msg
75
+ @@msg ||= {}
76
+ @@msg[self.lang] ||= self.find_msg
77
+ Thread.current[:msg] ||= @@msg[self.lang]
78
+ end
79
+
80
+ def self.find_msg(lang = self.lang)
81
+ lang.each {|range|
82
+ [
83
+ range,
84
+ range.sub(/-.*/, ''),
85
+ ].uniq.each {|r|
86
+ po_file = ::File.join(self.po_dir, r, "#{self.domain}.po")
87
+ return ::File.open(po_file, ((RUBY_VERSION < '1.9') ? 'r' : 'r:utf-8')) {|f|
88
+ self.parse_msg f
89
+ } if ::File.readable? po_file
90
+ return {} if r == 'en' # default
91
+ }
92
+ }
93
+ {}
94
+ end
95
+
96
+ def self.parse_msg(f)
97
+ msg = {}
98
+ msgid = nil
99
+ f.each_line {|line|
100
+ case line
101
+ when REX::COMMENT_FUZZY
102
+ msgid = :skip_next
103
+ when REX::COMMENT
104
+ next
105
+ when REX::PLURAL_EXPRESSION
106
+ msg[:plural] = instance_eval "Proc.new {|n| #{$1} }"
107
+ when REX::MSGID
108
+ msgid = (msgid == :skip_next) ? :skip : $1
109
+ when REX::MSGSTR_PLURAL
110
+ if msgid.is_a? ::String
111
+ msg[msgid] = [] unless msg[msgid].is_a? ::Array
112
+ msg[msgid][$1.to_i] = $2
113
+ end
114
+ when REX::MSGSTR
115
+ msg[msgid] = $1 if msgid.is_a? ::String
116
+ end
117
+ }
118
+ msg
119
+ end
120
+
121
+ def self.merge_msg!(m)
122
+ m.delete :plural
123
+ Thread.current[:msg] = self.msg.merge m
124
+ end
125
+
126
+ module_function
127
+
128
+ def _(msgid)
129
+ Bike::I18n::Msgstr.new(Array(Bike::I18n.msg[msgid]).first || msgid)
130
+ end
131
+
132
+ def n_(msgid, msgid_plural, n)
133
+ msgstrs = Bike::I18n.msg[msgid].is_a?(::Array) ? Bike::I18n.msg[msgid] : [msgid, msgid_plural]
134
+ case v = Bike::I18n.msg[:plural] ? Bike::I18n.msg[:plural].call(n) : (n != 1)
135
+ when true
136
+ Bike::I18n::Msgstr.new msgstrs[1]
137
+ when false
138
+ Bike::I18n::Msgstr.new msgstrs[0]
139
+ else
140
+ Bike::I18n::Msgstr.new msgstrs[v.to_i]
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,256 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Bike::Parser
7
+
8
+ module_function
9
+
10
+ def parse_html(html, action = :index, xml = false)
11
+ item = {}
12
+
13
+ html = gsub_block(html, '(?:app|runo)-\w+') {|open, inner, close| # "runo" is obsolete at 0.1.1
14
+ id = open[/id="(.+?)"/i, 1] || 'main'
15
+ item[id] = parse_block(open, inner, close, action, xml)
16
+ "$(#{id})"
17
+ }
18
+ html = gsub_action_tmpl(html) {|id, act, open, inner, close|
19
+ id ||= 'main'
20
+ if item[id]
21
+ item[id][:tmpl] ||= {}
22
+ inner = gsub_action_tmpl(inner) {|i, a, *t|
23
+ item[id][:tmpl][a] = t.join
24
+ "$(.#{a})"
25
+ }
26
+ item[id][:tmpl][act] = open + inner + close
27
+ "$(#{id}.#{act})"
28
+ else
29
+ open + inner + close
30
+ end
31
+ }
32
+ item.each {|id, meta|
33
+ m = Proc.new {|a| html.include?("$(#{id}.#{a})") || meta[:tmpl][action].include?("$(.#{a})") }
34
+ supplement_sd(meta[:tmpl][action], meta[:workflow], m)
35
+ html.sub!("$(#{id})", "$(#{id}.message)\\&") unless (
36
+ meta[:workflow] == 'attachment' || m.call('message')
37
+ )
38
+ } unless xml
39
+
40
+ html = gsub_scalar(html) {|id, meta|
41
+ item[id] = meta
42
+ "$(#{id})"
43
+ }
44
+ html.gsub!(/\s*class=".*?"/,'') if xml
45
+
46
+ meta = scrape_meta html
47
+ meta.merge(
48
+ :label => meta[:label] || scrape_label(html),
49
+ :item => item,
50
+ :tmpl => {action => html}
51
+ )
52
+ end
53
+
54
+ def parse_xml(html, action = :index)
55
+ parse_html(html, action, xml = true)
56
+ end
57
+
58
+ def gsub_action_tmpl(html, &block)
59
+ rex_klass = /(?:\w+\-)?(?:action|view|navi|submit|done)\w*/
60
+ gsub_block(html, rex_klass) {|open, inner, close|
61
+ klass = open[/class=(?:"|"[^"]*?\s)(#{rex_klass})(?:"|\s)/, 1]
62
+ id, action = (klass =~ /-/) ? klass.split('-', 2) : [nil, klass]
63
+ block.call(id, action.intern, open, inner, close)
64
+ }
65
+ end
66
+
67
+ def gsub_block(html, class_name, &block)
68
+ rex_open_tag = /\s*<(\w+)[^>]+?class=(?:"|"[^"]*?\s)#{class_name}(?:"|\s).*?>\n?/i
69
+ out = ''
70
+ s = StringScanner.new html
71
+ until s.eos?
72
+ out << skip_comment(s)
73
+ if s.scan rex_open_tag
74
+ open_tag = s[0]
75
+ inner_html, close_tag = scan_inner_html(s, s[1])
76
+ close_tag << "\n" if s.scan /\n/
77
+ out << block.call(open_tag, inner_html, close_tag)
78
+ else
79
+ out << s.scan(/.+?(?=\t| |<|\z)/m).to_s
80
+ end
81
+ end
82
+ out
83
+ end
84
+
85
+ def gsub_scalar(html, &block)
86
+ out = ''
87
+ s = StringScanner.new html
88
+ until s.eos?
89
+ out << skip_comment(s)
90
+ if s.scan /\$\((\w+)(?:\s+|\s*=\s*)([\w\-]+)\s*/m
91
+ out << block.call(s[1], {:klass => s[2]}.merge(scan_tokens(s)))
92
+ else
93
+ out << s.scan(/.+?(?=\$|<!|\z)/m).to_s
94
+ end
95
+ end
96
+ out
97
+ end
98
+
99
+ def skip_comment(s)
100
+ if s.scan /\s*<(!--|!\[CDATA\[)\n*/
101
+ s[0] + scan_inner_html(s, s[1]).join + (s.scan(/\n/) || '')
102
+ else
103
+ ''
104
+ end
105
+ end
106
+
107
+ def scrape_meta(html)
108
+ meta = {}
109
+ html.gsub!(/(?:^\s+)?<meta[^>]*name="bike-([^"]+)[^>]*content="([^"]+).*?>\n?/i) {
110
+ meta[$1.intern] = $2.include?(',') ? $2.split(/\s*,\s*/) : $2
111
+ ''
112
+ }
113
+ meta
114
+ end
115
+
116
+ def scrape_label(html)
117
+ if html.sub!(/\A((?:[^<]*<!--)?[^<]*<[^>]*title=")([^"]+)/, '\\1')
118
+ label_plural = $2.to_s.split(/,/).collect {|s| s.strip }
119
+ label_plural *= 4 if label_plural.size == 1
120
+ label = label_plural.first
121
+ Bike::I18n.msg[label] ||= label_plural
122
+ label
123
+ end
124
+ end
125
+
126
+ def supplement_sd(tmpl, workflow, m)
127
+ tmpl << '$(.navi)' unless m.call 'navi'
128
+ unless workflow == 'attachment'
129
+ tmpl << '$(.submit)' unless m.call 'submit'
130
+ tmpl << '$(.action_create)' unless m.call 'action_create'
131
+ end
132
+ tmpl
133
+ end
134
+
135
+ def supplement_ss(tmpl, action)
136
+ if action == :summary
137
+ tmpl.sub!(
138
+ /\$\(.*?\)/m,
139
+ '<a href="$(.uri_detail)">\&</a>'
140
+ ) unless tmpl.include? '$(.uri_detail)'
141
+ else
142
+ tmpl.sub!(
143
+ /\$\([^\.]*?\)/m,
144
+ '$(.a_update)\&</a>'
145
+ ) unless tmpl.include? '$(.action_update)'
146
+ tmpl.sub!(
147
+ /.*\$\(.*?\)/m,
148
+ '\&$(.hidden)'
149
+ ) unless tmpl.include? '$(.hidden)'
150
+ end
151
+ tmpl
152
+ end
153
+
154
+ def parse_block(open_tag, inner_html, close_tag, action = :index, xml = false)
155
+ open_tag.sub!(/id=".*?"/i, 'id="@(name)"')
156
+ workflow = open_tag[/class=(?:"|".*?\s)(?:app|runo)-(\w+)/, 1] # "runo" is obsolete at 0.1.1
157
+
158
+ if inner_html =~ /<(?:\w+).+?class=(?:"|"[^"]*?\s)(model|body)(?:"|\s)/i # "body" is obsolete at 0.1.1
159
+ item_html = ''
160
+ sd_tmpl = gsub_block(inner_html, Regexp.last_match(1)) {|open, inner, close|
161
+ item_html = open + inner + close
162
+ '$()'
163
+ }
164
+ else
165
+ item_html = inner_html
166
+ sd_tmpl = '$()'
167
+ end
168
+
169
+ tmpl = {}
170
+ sd_tmpl = gsub_action_tmpl(sd_tmpl) {|id, act, open, inner, close|
171
+ inner = gsub_action_tmpl(inner) {|i, a, *t|
172
+ tmpl[a] = t.join
173
+ "$(.#{a})"
174
+ }
175
+ tmpl[act] = open + inner + close
176
+ "$(.#{act})"
177
+ }
178
+
179
+ tmpl[action] = "#{open_tag}#{sd_tmpl}#{close_tag}"
180
+ tmpl[action].gsub!(/\s*class=".*?"/,'') if xml
181
+
182
+ item_meta = Bike::Parser.parse_html(item_html, action, xml)
183
+ supplement_ss(item_meta[:tmpl][action], action) unless xml || workflow == 'attachment'
184
+
185
+ sd = {
186
+ :klass => 'set-dynamic',
187
+ :workflow => workflow.downcase,
188
+ :tmpl => tmpl,
189
+ :item => {
190
+ 'default' => item_meta,
191
+ },
192
+ }
193
+ (inner_html =~ /\A\s*<!--(.+?)-->/m) ? sd.merge(scan_tokens(StringScanner.new($1))) : sd
194
+ end
195
+
196
+ def parse_token(prefix, token, meta = {})
197
+ case prefix
198
+ when ':'
199
+ meta[:default] = token
200
+ when ';'
201
+ meta[:defaults] ||= []
202
+ meta[:defaults] << token
203
+ when ','
204
+ meta[:options] ||= []
205
+ meta[:options] << token
206
+ else
207
+ case token
208
+ when /^(-?\d+)?\.\.(-?\d+)?$/
209
+ meta[:min] = $1.to_i if $1
210
+ meta[:max] = $2.to_i if $2
211
+ when /^(\d+)\*(\d+)$/
212
+ meta[:width] = $1.to_i
213
+ meta[:height] = $2.to_i
214
+ else
215
+ meta[:tokens] ||= []
216
+ meta[:tokens] << token
217
+ end
218
+ end
219
+ meta
220
+ end
221
+
222
+ def scan_inner_html(s, name)
223
+ open_tag = "<#{name}"
224
+ close_tag = {'!--' => '-->', '<![CDATA[' => ']]>'}[name] || "</#{name}>"
225
+ contents = ''
226
+ rex = /(.*?)(#{Regexp.quote(open_tag)}|#{Regexp.quote(close_tag)}|\z)/m
227
+ gen = 1
228
+ until s.eos? || (gen < 1)
229
+ contents << s.scan(rex)
230
+ gen += 1 if s[2] == open_tag
231
+ gen -= 1 if s[2] == close_tag
232
+ end
233
+ contents.gsub!(/\A\n+/, '')
234
+ contents.gsub!(/[\t ]*#{Regexp.quote(close_tag)}\z/, '')
235
+ [contents, $&]
236
+ end
237
+
238
+ def scan_tokens(s)
239
+ meta = {}
240
+ until s.eos? || s.scan(/\)/)
241
+ prefix = s[1] if s.scan /([:;,])?\s*/
242
+ if s.scan /(["'])(.*?)(\1|$)/
243
+ token = s[2]
244
+ elsif s.scan /[^\s\):;,]+/
245
+ token = s[0]
246
+ end
247
+ prefix ||= ',' if s.scan /(?=,)/ # 1st element of options
248
+ prefix ||= ';' if s.scan /(?=;)/ # 1st element of defaults
249
+
250
+ parse_token(prefix, token, meta)
251
+ s.scan /\s+/
252
+ end
253
+ meta
254
+ end
255
+
256
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Bike::Path
7
+
8
+ module_function
9
+
10
+ def tid_of(path)
11
+ path[Bike::REX::TID]
12
+ end
13
+
14
+ def steps_of(path)
15
+ _dirname(path).gsub(Bike::REX::PATH_ID, '').split('/').select {|step_or_cond|
16
+ step_or_cond != '' &&
17
+ step_or_cond !~ Regexp.union(Bike::REX::COND, Bike::REX::COND_D, Bike::REX::TID)
18
+ }
19
+ end
20
+
21
+ def base_of(path)
22
+ base = Bike::Set::Static::Folder.root.item steps_of(path)
23
+ if base.is_a? Bike::Set::Static::Folder
24
+ base.item('main') || base.find {|item| item.is_a? Bike::Set::Dynamic } || base
25
+ else
26
+ base
27
+ end
28
+ end
29
+
30
+ def conds_of(path)
31
+ dir = _dirname path.gsub(Bike::REX::PATH_ID, '')
32
+ conds = $& ? {:id => sprintf('%.8d_%.4d', $1, $2)} : {}
33
+
34
+ dir.split('/').inject(conds) {|conds, step_or_cond|
35
+ if step_or_cond =~ Bike::REX::COND
36
+ conds[$1.intern] = $2
37
+ elsif step_or_cond =~ Bike::REX::COND_D
38
+ conds[:d] = $&
39
+ end
40
+ conds
41
+ }
42
+ end
43
+
44
+ def action_of(path)
45
+ a = _basename(path).to_s[/^[a-z]+/]
46
+ a.intern if a && a != 'index'
47
+ end
48
+
49
+ def sub_action_of(path)
50
+ a = _basename(path).to_s[/_([a-z]+)/, 1]
51
+ a.intern if a
52
+ end
53
+
54
+ def path_of(conds)
55
+ conds = {} unless conds.is_a? ::Hash
56
+ (
57
+ (conds.keys - [:order, :p, :id]) |
58
+ ([:order, :p, :id] & conds.keys)
59
+ ).collect {|cid|
60
+ if cid == :id
61
+ ids = Array(conds[:id]).collect {|id|
62
+ (id =~ /_(#{Bike::REX::ID_SHORT})$/) ? $1 : (id if Bike::REX::ID)
63
+ }.compact
64
+ if (ids.size == 1) && (ids.first =~ Bike::REX::ID)
65
+ '%s/%d/' % [$1, $2.to_i]
66
+ elsif ids.size > 0
67
+ "id=#{ids.join ','}/"
68
+ end
69
+ elsif cid == :d
70
+ conds[:id] ? '' : "#{conds[:d]}/"
71
+ else
72
+ "#{cid}=#{Array(conds[cid]).join ','}/"
73
+ end
74
+ }.join.sub(%r{/p=1/$}, '/')
75
+ end
76
+
77
+ def _dirname(path) # returns '/foo/bar/' for '/foo/bar/'
78
+ path[%r{^.*/}] || ''
79
+ end
80
+
81
+ def _basename(path) # returns nil for '/foo/bar/'
82
+ path[%r{[^/]+$}]
83
+ end
84
+
85
+ end
86
+