runo 0.1.0

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 (149) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/bin/runo +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +141 -0
  7. data/lib/_parser.rb +243 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +213 -0
  10. data/lib/_storage/file.rb +200 -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/meta/_meta.rb +20 -0
  32. data/lib/meta/group.rb +19 -0
  33. data/lib/meta/id.rb +59 -0
  34. data/lib/meta/owner.rb +21 -0
  35. data/lib/meta/timestamp.rb +118 -0
  36. data/lib/runo.rb +396 -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 +195 -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 +75 -0
  95. data/skel/skin/index.html +41 -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/sub-20100306_0001.yaml +3 -0
  111. data/t/skin/index.yaml +3 -0
  112. data/t/skin/t_attachment/index.html +13 -0
  113. data/t/skin/t_contact/done.html +6 -0
  114. data/t/skin/t_contact/index.html +9 -0
  115. data/t/skin/t_file/index.html +16 -0
  116. data/t/skin/t_img/index.html +14 -0
  117. data/t/skin/t_img/test.jpg +0 -0
  118. data/t/skin/t_select/index.html +9 -0
  119. data/t/skin/t_store/index.html +9 -0
  120. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  121. data/t/skin/t_summary/create.html +9 -0
  122. data/t/skin/t_summary/index.html +9 -0
  123. data/t/skin/t_summary/summary.html +9 -0
  124. data/t/t.rb +27 -0
  125. data/t/test_checkbox.rb +273 -0
  126. data/t/test_field.rb +330 -0
  127. data/t/test_file.rb +900 -0
  128. data/t/test_id.rb +215 -0
  129. data/t/test_img.rb +328 -0
  130. data/t/test_meta.rb +57 -0
  131. data/t/test_parser.rb +1266 -0
  132. data/t/test_password.rb +188 -0
  133. data/t/test_radio.rb +226 -0
  134. data/t/test_role.rb +249 -0
  135. data/t/test_runo.rb +742 -0
  136. data/t/test_runo_call.rb +1286 -0
  137. data/t/test_runo_i18n.rb +318 -0
  138. data/t/test_select.rb +182 -0
  139. data/t/test_set_complex.rb +527 -0
  140. data/t/test_set_dynamic.rb +1504 -0
  141. data/t/test_set_folder.rb +515 -0
  142. data/t/test_set_permit.rb +246 -0
  143. data/t/test_set_static.rb +445 -0
  144. data/t/test_storage.rb +915 -0
  145. data/t/test_text.rb +125 -0
  146. data/t/test_textarea.rb +138 -0
  147. data/t/test_timestamp.rb +473 -0
  148. data/t/test_workflow.rb +367 -0
  149. metadata +345 -0
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Text < Runo::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].first =~ /^\d+$/
10
+ super
11
+ end
12
+
13
+ def errors
14
+ if (my[:max].to_i > 0) && (val.size > my[:max])
15
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
16
+ elsif (my[:min].to_i == 1) && val.empty?
17
+ [_ 'mandatory']
18
+ elsif (my[:min].to_i > 0) && (val.size < my[:min])
19
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
20
+ else
21
+ []
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def _g_update(arg)
28
+ <<_html.chomp
29
+ <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Runo::Field.h val}" size="#{my[:size]}" />#{_g_errors arg}</span>
30
+ _html
31
+ end
32
+ alias :_g_create :_g_update
33
+
34
+ def val_cast(v)
35
+ v.to_s
36
+ end
37
+
38
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Textarea < Runo::Field
7
+
8
+ def errors
9
+ if (my[:max].to_i > 0) && (val.size > my[:max])
10
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
11
+ elsif (my[:min].to_i == 1) && val.empty?
12
+ [_ 'mandatory']
13
+ elsif (my[:min].to_i > 0) && (val.size < my[:min])
14
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
15
+ else
16
+ []
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def _g_update(arg)
23
+ <<_html
24
+ <span class="#{_g_class arg}">
25
+ <textarea name="#{my[:short_name]}" cols="#{my[:width]}" rows="#{my[:height]}">#{Runo::Field.h val}</textarea>
26
+ #{_g_errors arg}</span>
27
+ _html
28
+ end
29
+ alias :_g_create :_g_update
30
+
31
+ def val_cast(v)
32
+ v.to_s.gsub(/\r\n?/, "\n")
33
+ end
34
+
35
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ require 'rack/utils'
7
+
8
+ class Runo::Textarea::Pre < Runo::Textarea
9
+
10
+ def _g_default(arg)
11
+ '<pre>' + Runo::Field.h(val) + '</pre>'
12
+ end
13
+
14
+ end
@@ -0,0 +1,173 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require 'csv'
7
+
8
+ class Runo::Textarea::Wiki < Runo::Textarea
9
+
10
+ private
11
+
12
+ def _g_default(arg)
13
+ wiki @val.to_s
14
+ end
15
+
16
+ def wiki(str)
17
+ str.chomp!
18
+
19
+ html = ''
20
+ body = ''
21
+ type = :p
22
+
23
+ Runo::Field.h(str).each_line {|line|
24
+ if line =~ /^---$/
25
+ new_type = :hr
26
+ else
27
+ line =~ /^(.?)/
28
+ case $1
29
+ when '!'
30
+ new_type = :heading
31
+ when ','
32
+ new_type = :table
33
+ when '*'
34
+ new_type = :ul
35
+ when '+'
36
+ new_type = :ol
37
+ when ' ', "\t"
38
+ new_type = :pre
39
+ when ''
40
+ new_type = :blank
41
+ else
42
+ new_type = :p
43
+ end
44
+ end
45
+ if new_type != type
46
+ html += __send__("element_#{type}", body.to_s)
47
+ body = ''
48
+ end
49
+ body += line
50
+ type = new_type
51
+ }
52
+ html += __send__("element_#{type}", body) if body != ''
53
+
54
+ html
55
+ end
56
+
57
+ def element_hr(body)
58
+ body.gsub!(/\n*\Z/, '')
59
+ body.gsub!(/^---$/, "<hr />\n")
60
+
61
+ <<_html
62
+ #{body}
63
+ _html
64
+ end
65
+
66
+ def element_heading(body)
67
+ <<_html
68
+ <h3>#{body}</h3>
69
+ _html
70
+ end
71
+
72
+ def element_table(body)
73
+ table = CSV.parse(body.gsub!(/^,/, ''))
74
+
75
+ table.each_with_index {|row, y|
76
+ row.each_index {|x|
77
+ if (row[x] == :same_as_above)
78
+ row[x] = nil
79
+ next
80
+ end
81
+
82
+ span = 1
83
+ ((y + 1) .. (table.size - 1)).each {|y_below|
84
+ if (table[y_below][x] == row[x])
85
+ table[y_below][x] = :same_as_above
86
+ span += 1
87
+ else
88
+ break
89
+ end
90
+ }
91
+ rowspan = (span > 1) ? " rowspan=\"#{span}\"" : ''
92
+
93
+ row[x] = "<td valign=\"top\"#{rowspan}>#{inline(row[x])}</td>"
94
+ }
95
+ }
96
+
97
+ rows = table.collect {|row|
98
+ " <tr>" + row.join('') + '</tr>'
99
+ }.join("\n")
100
+
101
+ <<_html
102
+ <table class="wiki">
103
+ #{rows}
104
+ </table>
105
+ _html
106
+ end
107
+
108
+ def element_list(body, ul_ol)
109
+ items = []
110
+ lines = []
111
+ type = nil
112
+ (body + "\n\n").each_line {|line|
113
+ line.sub!(/^(\*|\+)/, '')
114
+
115
+ unless line =~ /^(\*|\+)/ || lines.empty?
116
+ item = inline(lines.shift.to_s.chomp)
117
+ unless lines.empty?
118
+ lines = lines.join
119
+ item += (lines =~ /\A\*/) ? element_ul(lines) : element_ol(lines)
120
+ end
121
+ items << "<li>#{item}</li>\n" if item != ''
122
+ lines = []
123
+ end
124
+
125
+ lines << line
126
+ }
127
+
128
+ <<_html
129
+ <#{ul_ol} class="wiki">
130
+ #{items}</#{ul_ol}>
131
+ _html
132
+ end
133
+
134
+ def element_ul(body)
135
+ element_list(body, 'ul')
136
+ end
137
+
138
+ def element_ol(body)
139
+ element_list(body, 'ol')
140
+ end
141
+
142
+ def element_pre(body)
143
+ body.gsub!(/\n*\Z/, '')
144
+
145
+ <<_html
146
+ <pre class="wiki">#{body}
147
+ </pre>
148
+ _html
149
+ end
150
+
151
+ def element_p(body)
152
+ body.gsub!(/\n*\Z/, '')
153
+ body.gsub!(/\n/, "<br />\n")
154
+ body.gsub!(/^/, ' ')
155
+
156
+ <<_html
157
+ <p class="wiki">
158
+ #{inline(body)}
159
+ </p>
160
+ _html
161
+ end
162
+
163
+ def element_blank(body)
164
+ body.gsub!(/\n/, "<br />\n")
165
+ end
166
+
167
+ def inline(body)
168
+ body.gsub!(/\*(.+?)\*/, '<strong class="wiki">\1</strong>')
169
+ body.gsub!(/\=\=(.+?)\=\=/, '<s class="wiki">\1</s>')
170
+ body
171
+ end
172
+
173
+ end
@@ -0,0 +1,195 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Runo::Set
7
+
8
+ include Enumerable
9
+
10
+ def meta_dir
11
+ my[:folder][:dir] if my[:folder]
12
+ end
13
+
14
+ def meta_path
15
+ ['main', nil].include?(my[:name]) ?
16
+ my[:dir] :
17
+ "#{my[:dir]}/#{my[:name].sub(/^main-?/, '').gsub('-', '/')}"
18
+ end
19
+
20
+ def meta_base_path
21
+ Runo.base ? Runo.base[:path] : my[:path]
22
+ end
23
+
24
+ def val(*steps)
25
+ if steps.empty?
26
+ _val
27
+ elsif i = item(steps)
28
+ i.val
29
+ end
30
+ end
31
+
32
+ def item(*item_steps)
33
+ item_steps = item_steps.first if item_steps.first.is_a? ::Array
34
+ return self if item_steps.empty?
35
+
36
+ id, *item_steps = item_steps
37
+
38
+ if id.is_a?(::String) && child = collect_item(:id => id).first
39
+ item = item_steps.empty? ? child : child.item(*item_steps)
40
+ block_given? ? yield(item) : item
41
+ end
42
+ end
43
+
44
+ def pending?
45
+ !pending_items.empty? || action
46
+ end
47
+
48
+ def errors
49
+ return {} if
50
+ my[:id] =~ Runo::REX::ID_NEW &&
51
+ my[:parent].is_a?(Runo::Set::Dynamic) &&
52
+ my[:parent].workflow.is_a?(Runo::Workflow::Attachment)
53
+
54
+ errors = {}
55
+ @item_object.each_pair {|id, item|
56
+ errors[id] = item.errors unless item.valid? || item.action == :delete
57
+ }
58
+ errors
59
+ end
60
+
61
+ def collect(&block)
62
+ collect_item({}, &block)
63
+ end
64
+
65
+ def each(&block)
66
+ collect_item.each &block
67
+ end
68
+
69
+ def inspect_items(indent = 0)
70
+ my_action = action.inspect
71
+ my_result = result.is_a?(::Hash) ?
72
+ result.keys.sort.inspect.sub(/\A\[([\w\W]*)\]\z/, '{\1}') :
73
+ result.inspect
74
+
75
+ ' ' * indent +
76
+ "<\"#{my[:id]}\" @action=#{my_action} @result=#{my_result}>\n" +
77
+ @item_object.keys.sort.collect {|id|
78
+ item = @item_object[id]
79
+ if item.respond_to? :inspect_items
80
+ item.inspect_items(indent + 1)
81
+ else
82
+ action = item.action.inspect
83
+ result = item.result.inspect
84
+ val = item.val.inspect
85
+ ' ' * (indent + 1) + "<\"#{id}\" @action=#{action} @result=#{result} @val=#{val}>\n"
86
+ end
87
+ }.join
88
+ end
89
+
90
+ private
91
+
92
+ def _get(arg)
93
+ if respond_to?("_g_#{arg[:action]}", true)
94
+ _get_by_method arg
95
+ elsif summary?(arg) && action_tmpl = my[:tmpl][:summary]
96
+ _get_by_tmpl(arg, action_tmpl)
97
+ elsif action_tmpl = my[:tmpl][arg[:sub_action]] || my[:tmpl][arg[:action]]
98
+ _get_by_tmpl(arg, action_tmpl)
99
+ else
100
+ _get_by_tmpl(arg, my[:tmpl][:index])
101
+ end
102
+ end
103
+
104
+ def _get_by_tmpl(arg, tmpl = '')
105
+ tmpl.gsub(/@\((.+?)\)/) {
106
+ steps = $1.split '-'
107
+ id = steps.pop
108
+ item = item steps
109
+ item ? item[id.intern] : '???'
110
+ }.gsub(/_\((.+?)\)/) {
111
+ _ $1
112
+ }.gsub(/\$\((.*?)(?:\.([\w\-]+))?\)/) {
113
+ name, action = $1, $2
114
+ if name == ''
115
+ self_arg = action ?
116
+ arg.merge(:orig_action => arg[:action], :action => action.intern) : arg
117
+ _get_by_self_reference self_arg
118
+ else
119
+ steps = name.split '-'
120
+ item_arg = item_arg(arg, steps)
121
+ item = item steps
122
+ if item.nil?
123
+ '???'
124
+ elsif action
125
+ item_arg = item_arg.merge(
126
+ :orig_action => item_arg[:action],
127
+ :action => action.intern
128
+ )
129
+ item.send(:_get_by_self_reference, item_arg) # skip the authorization
130
+ else
131
+ item.get(item_arg)
132
+ end
133
+ end
134
+ }.gsub(/^\s+\n/, '')
135
+ end
136
+
137
+ def _get_by_self_reference(arg)
138
+ return if arg[:action].to_s =~ /^action_/ && ![:read, nil].include?(arg[:orig_action])
139
+ _get_by_method(arg)
140
+ end
141
+
142
+ def _get_by_action_tmpl(arg)
143
+ return nil unless !arg[:recur] && action_tmpl = my[:tmpl][arg[:action]]
144
+ _get_by_tmpl(arg.merge(:action => nil, :sub_action => nil, :recur => true), action_tmpl)
145
+ end
146
+
147
+ def _g_default(arg, &block)
148
+ collect_item(arg[:conds] || {}) {|item|
149
+ item_arg = item_arg(arg, item[:id])
150
+ next if item.empty? && ![:create, :update].include?(item_arg[:action])
151
+ block ? block.call(item, item_arg) : item.get(item_arg)
152
+ }
153
+ end
154
+
155
+ def _g_errors(arg)
156
+ # errors are shown by scalars
157
+ end
158
+
159
+ def item_arg(arg, steps)
160
+ steps.to_a.inject(arg) {|a, s|
161
+ i = a[s] || {}
162
+ i[:p_action] = a[:action]
163
+ unless i[:action]
164
+ i[:action] = a[:action]
165
+ i[:sub_action] = a[:sub_action] if a[:sub_action]
166
+ end
167
+ i
168
+ }
169
+ end
170
+
171
+ def summary?(arg)
172
+ [:read, nil].include?(arg[:action]) && !arg[:sub_action]
173
+ end
174
+
175
+ def permit_post?(action, val)
176
+ super || val.all? {|id, v|
177
+ if id.is_a? ::Symbol
178
+ true # not a item value
179
+ elsif id =~ Runo::REX::ID_NEW
180
+ permit? :create
181
+ else
182
+ item_action = v[:action] || :update
183
+ item(id) && item(id).permit?(item_action)
184
+ end
185
+ }
186
+ end
187
+
188
+ def pending_items
189
+ @item_object.keys.sort.inject({}) {|h, id|
190
+ h[id] = @item_object[id] if @item_object[id].pending?
191
+ h
192
+ }
193
+ end
194
+
195
+ end