runo 0.2.1 → 0.2.2

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/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