bike 0.2.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 (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
+