runo 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. data/README.rdoc +1 -118
  2. data/bin/runo +4 -29
  3. data/lib/dummy.rb +0 -0
  4. metadata +12 -278
  5. data/lib/_error.rb +0 -14
  6. data/lib/_field.rb +0 -260
  7. data/lib/_i18n.rb +0 -144
  8. data/lib/_parser.rb +0 -256
  9. data/lib/_path.rb +0 -86
  10. data/lib/_storage/_storage.rb +0 -215
  11. data/lib/_storage/file.rb +0 -201
  12. data/lib/_storage/sequel.rb +0 -174
  13. data/lib/_storage/temp.rb +0 -73
  14. data/lib/_widget/action_create.rb +0 -23
  15. data/lib/_widget/action_login.rb +0 -22
  16. data/lib/_widget/action_signup.rb +0 -16
  17. data/lib/_widget/action_update.rb +0 -16
  18. data/lib/_widget/crumb.rb +0 -24
  19. data/lib/_widget/done.rb +0 -16
  20. data/lib/_widget/login.rb +0 -25
  21. data/lib/_widget/me.rb +0 -31
  22. data/lib/_widget/message.rb +0 -51
  23. data/lib/_widget/navi.rb +0 -88
  24. data/lib/_widget/submit.rb +0 -49
  25. data/lib/_widget/view_ym.rb +0 -77
  26. data/lib/_workflow/_workflow.rb +0 -89
  27. data/lib/_workflow/attachment.rb +0 -50
  28. data/lib/_workflow/blog.rb +0 -28
  29. data/lib/_workflow/contact.rb +0 -23
  30. data/lib/_workflow/forum.rb +0 -26
  31. data/lib/_workflow/register.rb +0 -39
  32. data/lib/meta/_meta.rb +0 -20
  33. data/lib/meta/group.rb +0 -19
  34. data/lib/meta/id.rb +0 -59
  35. data/lib/meta/owner.rb +0 -21
  36. data/lib/meta/timestamp.rb +0 -118
  37. data/lib/runo.rb +0 -396
  38. data/lib/scalar/checkbox.rb +0 -68
  39. data/lib/scalar/file.rb +0 -144
  40. data/lib/scalar/img.rb +0 -112
  41. data/lib/scalar/password.rb +0 -58
  42. data/lib/scalar/radio.rb +0 -47
  43. data/lib/scalar/select.rb +0 -47
  44. data/lib/scalar/text.rb +0 -38
  45. data/lib/scalar/textarea.rb +0 -35
  46. data/lib/scalar/textarea_pre.rb +0 -14
  47. data/lib/scalar/textarea_wiki.rb +0 -173
  48. data/lib/set/_set.rb +0 -196
  49. data/lib/set/dynamic.rb +0 -177
  50. data/lib/set/static.rb +0 -102
  51. data/lib/set/static_folder.rb +0 -96
  52. data/locale/en/index.po +0 -242
  53. data/locale/index.pot +0 -243
  54. data/locale/ja/index.po +0 -242
  55. data/locale/lazy_parser.rb +0 -54
  56. data/skel/config.ru +0 -27
  57. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  59. data/skel/skin/_users/00000000_frank.yaml +0 -12
  60. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  61. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  62. data/skel/skin/_users/00000000_root.yaml +0 -11
  63. data/skel/skin/_users/css/users.css +0 -21
  64. data/skel/skin/_users/css/users.less +0 -25
  65. data/skel/skin/_users/done.html +0 -42
  66. data/skel/skin/_users/index.html +0 -46
  67. data/skel/skin/_users/index.yaml +0 -3
  68. data/skel/skin/_users/summary.html +0 -40
  69. data/skel/skin/css/base.css +0 -93
  70. data/skel/skin/css/base.less +0 -139
  71. data/skel/skin/css/coax.css +0 -199
  72. data/skel/skin/css/coax.less +0 -244
  73. data/skel/skin/examples/blog/20091214_0001.yaml +0 -8
  74. data/skel/skin/examples/blog/20100630_0001.yaml +0 -8
  75. data/skel/skin/examples/blog/20100630_0002.yaml +0 -14
  76. data/skel/skin/examples/blog/20100701_0001.yaml +0 -8
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  79. data/skel/skin/examples/blog/20100701_0002.yaml +0 -19
  80. data/skel/skin/examples/blog/frank/20100701_0001.yaml +0 -10
  81. data/skel/skin/examples/blog/frank/index.yaml +0 -4
  82. data/skel/skin/examples/blog/index.html +0 -51
  83. data/skel/skin/examples/blog/rss.xml +0 -18
  84. data/skel/skin/examples/contact/20100701_0001-file.txt +0 -1
  85. data/skel/skin/examples/contact/20100701_0001.yaml +0 -15
  86. data/skel/skin/examples/contact/20100701_0002.yaml +0 -8
  87. data/skel/skin/examples/contact/20100701_0003.yaml +0 -9
  88. data/skel/skin/examples/contact/index.html +0 -47
  89. data/skel/skin/examples/contact/js/contact.js +0 -13
  90. data/skel/skin/examples/contact/summary.html +0 -54
  91. data/skel/skin/examples/forum/20100701_0001.yaml +0 -41
  92. data/skel/skin/examples/forum/20100701_0002.yaml +0 -25
  93. data/skel/skin/examples/forum/index.html +0 -68
  94. data/skel/skin/examples/forum/summary.html +0 -47
  95. data/skel/skin/examples/index.html +0 -73
  96. data/skel/skin/index.html +0 -39
  97. data/skel/skin/js/base.js +0 -50
  98. data/t/locale/de/index.po +0 -19
  99. data/t/locale/en-GB/index.po +0 -25
  100. data/t/locale/ja/index.po +0 -30
  101. data/t/skin/_users/00000000_test.yaml +0 -3
  102. data/t/skin/_users/index.html +0 -13
  103. data/t/skin/foo/20091120_0001.yaml +0 -7
  104. data/t/skin/foo/bar/20091120_0001.yaml +0 -5
  105. data/t/skin/foo/bar/index.yaml +0 -5
  106. data/t/skin/foo/baz/css/baz.css +0 -1
  107. data/t/skin/foo/css/foo.css +0 -1
  108. data/t/skin/foo/index.html +0 -14
  109. data/t/skin/foo/index.yaml +0 -7
  110. data/t/skin/foo/not_css/foo.css +0 -1
  111. data/t/skin/foo/qux/index.html +0 -8
  112. data/t/skin/foo/qux/moo/index.html +0 -6
  113. data/t/skin/foo/sub-20100306_0001.yaml +0 -3
  114. data/t/skin/index.yaml +0 -3
  115. data/t/skin/t_attachment/index.html +0 -13
  116. data/t/skin/t_contact/done.html +0 -6
  117. data/t/skin/t_contact/index.html +0 -9
  118. data/t/skin/t_file/index.html +0 -16
  119. data/t/skin/t_img/index.html +0 -14
  120. data/t/skin/t_img/test.jpg +0 -0
  121. data/t/skin/t_select/index.html +0 -9
  122. data/t/skin/t_store/index.html +0 -9
  123. data/t/skin/t_summary/20100326_0001.yaml +0 -3
  124. data/t/skin/t_summary/create.html +0 -9
  125. data/t/skin/t_summary/index.html +0 -9
  126. data/t/skin/t_summary/summary.html +0 -9
  127. data/t/t.rb +0 -27
  128. data/t/test_checkbox.rb +0 -273
  129. data/t/test_field.rb +0 -330
  130. data/t/test_file.rb +0 -900
  131. data/t/test_id.rb +0 -215
  132. data/t/test_img.rb +0 -328
  133. data/t/test_meta.rb +0 -57
  134. data/t/test_parser.rb +0 -1516
  135. data/t/test_password.rb +0 -188
  136. data/t/test_radio.rb +0 -226
  137. data/t/test_role.rb +0 -249
  138. data/t/test_runo.rb +0 -768
  139. data/t/test_runo_call.rb +0 -1281
  140. data/t/test_runo_i18n.rb +0 -325
  141. data/t/test_select.rb +0 -182
  142. data/t/test_set_complex.rb +0 -527
  143. data/t/test_set_dynamic.rb +0 -1504
  144. data/t/test_set_folder.rb +0 -515
  145. data/t/test_set_permit.rb +0 -246
  146. data/t/test_set_static.rb +0 -468
  147. data/t/test_storage.rb +0 -915
  148. data/t/test_text.rb +0 -125
  149. data/t/test_textarea.rb +0 -138
  150. data/t/test_timestamp.rb +0 -473
  151. data/t/test_workflow.rb +0 -367
@@ -1,14 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- module Runo::Error
7
-
8
- class Forbidden < StandardError
9
- end
10
-
11
- class NotFound < StandardError
12
- end
13
-
14
- end
@@ -1,260 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
-
6
- require 'rubygems'
7
- require 'rack/utils'
8
-
9
- $KCODE = 'UTF8' if RUBY_VERSION < '1.9.0'
10
-
11
- class Runo::Field
12
-
13
- include Runo::I18n
14
-
15
- DEFAULT_META = {}
16
-
17
- def self.instance(meta = {})
18
- k = meta[:klass].to_s.split(/-/).inject(Runo) {|c, name|
19
- name = name.capitalize
20
- c.const_get(name)
21
- }
22
- k.new(meta) if k < self
23
- end
24
-
25
- def self.h(val)
26
- Rack::Utils.escape_html val.to_s
27
- end
28
-
29
- attr_reader :action, :result
30
-
31
- def initialize(meta = {})
32
- @meta = self.class.const_get(:DEFAULT_META).merge meta
33
- @val = val_cast(nil)
34
- end
35
-
36
- def inspect
37
- caller.grep(/`inspect'/).empty? ? super : "<#{self.class} name=\"#{my[:name]}\">"
38
- end
39
-
40
- def [](id, *arg)
41
- respond_to?("meta_#{id}") ? __send__("meta_#{id}", *arg) : @meta[id]
42
- end
43
-
44
- def []=(id, v)
45
- @meta[id] = v
46
- end
47
-
48
- def val
49
- @val
50
- end
51
-
52
- def item(*item_steps)
53
- item_steps = item_steps.first if item_steps.first.is_a? ::Array
54
- item_steps.empty? ? self : nil # scalar has no item
55
- end
56
-
57
- def find_ancestor(&block)
58
- f = self
59
- block.call(f) ? (return f) : (f = f[:parent]) until f.nil?
60
- end
61
-
62
- def meta_name
63
- my[:parent] && !my[:parent].is_a?(Runo::Set::Static::Folder) ?
64
- "#{my[:parent][:name]}-#{my[:id]}" : my[:id]
65
- end
66
-
67
- def meta_full_name
68
- my[:parent] ? "#{my[:parent][:full_name]}-#{my[:id]}" : my[:id]
69
- end
70
-
71
- def meta_short_name
72
- return '' if Runo.base && my[:full_name] == Runo.base[:full_name]
73
- my[:parent] && Runo.base && my[:parent][:full_name] != Runo.base[:full_name] ?
74
- "#{my[:parent][:short_name]}-#{my[:id]}" : my[:id]
75
- end
76
-
77
- def meta_folder
78
- find_ancestor {|f| f.is_a? Runo::Set::Static::Folder }
79
- end
80
-
81
- def meta_sd
82
- find_ancestor {|f| f.is_a? Runo::Set::Dynamic }
83
- end
84
-
85
- def meta_client
86
- Runo.client
87
- end
88
-
89
- def meta_owner
90
- @meta[:owner] || (my[:parent] ? my[:parent][:owner] : 'root')
91
- end
92
-
93
- def meta_owners
94
- (my[:parent] ? my[:parent][:owners] : ['root']) | Array(@meta[:owner])
95
- end
96
-
97
- def meta_admins
98
- (my[:parent] ? my[:parent][:admins] : ['root']) | Array(@meta[:admin])
99
- end
100
-
101
- def meta_group
102
- @meta[:group] || (my[:parent] ? my[:parent][:group] : [])
103
- end
104
-
105
- def meta_roles
106
- roles = 0
107
- roles |= Runo::Workflow::ROLE_ADMIN if my[:admins].include? my[:client]
108
- roles |= Runo::Workflow::ROLE_GROUP if my[:group].include? my[:client]
109
- roles |= Runo::Workflow::ROLE_OWNER if my[:owner] == my[:client]
110
- roles |= (Runo.client == 'nobody') ? Runo::Workflow::ROLE_NONE : Runo::Workflow::ROLE_USER
111
- roles
112
- end
113
-
114
- def permit?(action)
115
- return false if action == :create && @result == :load
116
- return true unless my[:sd]
117
- return true if my[:sd].workflow.permit?(my[:roles], action)
118
- return true if find_ancestor {|f| f[:id] =~ Runo::REX::ID_NEW } # descendant of a new item
119
- end
120
-
121
- def default_action
122
- return :read unless my[:sd]
123
- actions = my[:sd].workflow.class.const_get(:PERM).keys - [:read, :create, :update]
124
- ([:read, :create, :update] + actions).find {|action| permit? action }
125
- end
126
-
127
- def get(arg = {})
128
- if permit_get? arg
129
- _get(arg)
130
- else
131
- if arg[:action]
132
- raise Runo::Error::Forbidden
133
- else
134
- arg[:action] = default_action
135
- end
136
- arg[:action] ? _get(arg) : 'xxx'
137
- end
138
- end
139
-
140
- def load_default
141
- post :load_default
142
- end
143
-
144
- def load(v = nil)
145
- post :load, v
146
- end
147
-
148
- def create(v = nil)
149
- post :create, v
150
- end
151
-
152
- def update(v = nil)
153
- post :update, v
154
- end
155
-
156
- def delete
157
- post :delete
158
- end
159
-
160
- def post(action, v = nil)
161
- raise Runo::Error::Forbidden unless permit_post?(action, v)
162
-
163
- if _post action, val_cast(v)
164
- @result = (action == :load || action == :load_default) ? action : nil
165
- @action = (action == :load || action == :load_default) ? nil : action
166
- else
167
- @result = nil
168
- end
169
- self
170
- end
171
-
172
- def commit(type = :temp)
173
- if valid?
174
- @result = @action
175
- @action = nil if type == :persistent
176
- self
177
- end
178
- end
179
-
180
- def pending?
181
- @action ? true : false
182
- end
183
-
184
- def valid?
185
- errors.empty?
186
- end
187
-
188
- def empty?
189
- val.respond_to?(:empty?) ? val.empty? : (val.to_s == '')
190
- end
191
-
192
- def errors
193
- []
194
- end
195
-
196
- private
197
-
198
- def my
199
- self
200
- end
201
-
202
- def _get(arg)
203
- _get_by_method arg
204
- end
205
-
206
- def _get_by_method(arg)
207
- m = "_g_#{arg[:action]}"
208
- respond_to?(m, true) ? __send__(m, arg) : _g_default(arg)
209
- end
210
- alias :_get_by_self_reference :_get_by_method
211
-
212
- def _g_empty(arg)
213
- end
214
- alias :_g_login :_g_empty
215
- alias :_g_done :_g_empty
216
- alias :_g_message :_g_empty
217
-
218
- def _g_default(arg)
219
- Runo::Field.h val
220
- end
221
-
222
- def _g_errors(arg)
223
- <<_html unless _g_valid? arg
224
- <span class="error_message">#{errors.join "<br/>\n"}</span>
225
- _html
226
- end
227
-
228
- def _g_class(arg)
229
- c = self.class.to_s.sub('Runo::', '').gsub('::', '-').downcase
230
- _g_valid?(arg) ? c : "#{c} error"
231
- end
232
-
233
- def _g_valid?(arg)
234
- valid? || (arg[:action] == :create && !item.pending?)
235
- end
236
-
237
- def _post(action, v)
238
- case action
239
- when :load_default
240
- @val = val_cast(my[:defaults] || my[:default])
241
- when :load, :create, :update
242
- @val = v if @val != v
243
- when :delete
244
- true
245
- end
246
- end
247
-
248
- def permit_get?(arg)
249
- permit? arg[:action]
250
- end
251
-
252
- def permit_post?(action, v)
253
- action == :load || action == :load_default || permit?(action)
254
- end
255
-
256
- def val_cast(v)
257
- v
258
- end
259
-
260
- end
@@ -1,144 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- module Runo::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
- Runo::I18n::Msgstr.new(Array(Runo::I18n.msg[msgid]).first || msgid)
130
- end
131
-
132
- def n_(msgid, msgid_plural, n)
133
- msgstrs = Runo::I18n.msg[msgid].is_a?(::Array) ? Runo::I18n.msg[msgid] : [msgid, msgid_plural]
134
- case v = Runo::I18n.msg[:plural] ? Runo::I18n.msg[:plural].call(n) : (n != 1)
135
- when true
136
- Runo::I18n::Msgstr.new msgstrs[1]
137
- when false
138
- Runo::I18n::Msgstr.new msgstrs[0]
139
- else
140
- Runo::I18n::Msgstr.new msgstrs[v.to_i]
141
- end
142
- end
143
-
144
- end
@@ -1,256 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- module Runo::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="runo-([^"]+)[^>]*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
- Runo::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 = Runo::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