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,77 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_view_ym(arg)
11
+ return unless permit? :read
12
+
13
+ uris = _uri_ym arg
14
+ return unless uris && uris.size > 1
15
+
16
+ year_tmpl = month_tmpl = nil
17
+ div = my[:tmpl][:view_ym] || <<'_tmpl'
18
+ <div class="view_ym">
19
+ <span class="y">
20
+ $(.y) |
21
+ <span class="m">$()</span>
22
+ <br/>
23
+ </span>
24
+ </div>
25
+ _tmpl
26
+ div = Runo::Parser.gsub_block(div, 'y') {|open, inner, close|
27
+ inner = Runo::Parser.gsub_block(inner, 'm') {|*t|
28
+ month_tmpl = t.join
29
+ '$(.months)'
30
+ }
31
+ year_tmpl = open + inner + close
32
+ '$(.years)'
33
+ }
34
+ years = uris.inject({}) {|y, u|
35
+ year = u[/(\d{4})\d\d\/$/, 1]
36
+ y[year] ||= []
37
+ y[year] << u
38
+ y
39
+ }
40
+ p = (my[:order] =~ /^-/) ? 'p=last/' : ''
41
+ div.gsub('$(.years)') {
42
+ years.keys.sort.collect {|year|
43
+ year_tmpl.gsub('$(.y)', year).gsub('$(.months)') {
44
+ years[year].collect {|uri|
45
+ d = uri[/(\d{6})\//, 1]
46
+ y = d[/^\d{4}/]
47
+ m = d[/\d\d$/]
48
+ month_tmpl.gsub(/\$\((?:\.(ym|m))?\)/) {
49
+ label = ($1 == 'ym') ? _label_ym(y, m) : _label_m(m)
50
+ (arg[:conds][:d] == d) ?
51
+ "<span class=\"current\">#{label}</span>" :
52
+ "<a href=\"#{my[:path]}/#{uri}#{p}\">#{label}</a>"
53
+ }
54
+ }.join
55
+ }
56
+ }.join
57
+ }
58
+ end
59
+
60
+ def _uri_ym(arg)
61
+ @storage.__send__(:_sibs_d, :d => '000000').collect {|ym|
62
+ Runo::Path.path_of :d => ym
63
+ }
64
+ end
65
+
66
+ def _label_ym(y, m)
67
+ _('%{year}/%{month}') % {
68
+ :year => y,
69
+ :month => _label_m(m)
70
+ }
71
+ end
72
+
73
+ def _label_m(m)
74
+ _ Date::ABBR_MONTHNAMES[m.to_i]
75
+ end
76
+
77
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Workflow
7
+
8
+ include Runo::I18n
9
+
10
+ DEFAULT_META = {
11
+ :item_label => Runo::I18n.n_('item', 'items', 1),
12
+ }
13
+ DEFAULT_SUB_ITEMS = {}
14
+
15
+ ROLE_ADMIN = 0b10000
16
+ ROLE_GROUP = 0b01000
17
+ ROLE_OWNER = 0b00100
18
+ ROLE_USER = 0b00010
19
+ ROLE_NONE = 0b00001
20
+
21
+ PERM = {
22
+ :create => 0b11111,
23
+ :read => 0b11111,
24
+ :update => 0b11111,
25
+ :delete => 0b11111,
26
+ }
27
+
28
+ def self.instance(sd)
29
+ klass = sd[:workflow].to_s.capitalize
30
+ if klass != ''
31
+ self.const_get(klass).new sd
32
+ else
33
+ self.new sd
34
+ end
35
+ end
36
+
37
+ def self.roles(roles)
38
+ %w(admin group owner user none).select {|r|
39
+ roles & const_get("ROLE_#{r.upcase}") > 0
40
+ }.collect{|r| Runo::I18n._ r }
41
+ end
42
+
43
+ attr_reader :sd
44
+
45
+ def initialize(sd)
46
+ @sd = sd
47
+ end
48
+
49
+ def default_sub_items
50
+ self.class.const_get :DEFAULT_SUB_ITEMS
51
+ end
52
+
53
+ def permit?(roles, action)
54
+ case action
55
+ when :login, :done, :message
56
+ true
57
+ when :preview
58
+ # TODO: permit?(roles, action, sub_action = nil)
59
+ (roles & self.class.const_get(:PERM)[:read].to_i) > 0
60
+ else
61
+ (roles & self.class.const_get(:PERM)[action].to_i) > 0
62
+ end
63
+ end
64
+
65
+ def _get(arg)
66
+ @sd.instance_eval {
67
+ if arg[:action] == :create
68
+ item_instance '_001'
69
+ _get_by_tmpl({:action => :create, :conds => {:id => '_001'}}, my[:tmpl][:index])
70
+ end
71
+ }
72
+ end
73
+
74
+ def _hide?(arg)
75
+ (arg[:p_action] && arg[:p_action] != :read) ||
76
+ (arg[:orig_action] == :read && arg[:action] == :submit)
77
+ end
78
+
79
+ def before_commit
80
+ end
81
+
82
+ def after_commit
83
+ end
84
+
85
+ def next_action(base)
86
+ (!base.result || base.result.values.all? {|item| item.permit? :read }) ? :read_detail : :done
87
+ end
88
+
89
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Workflow::Attachment < Runo::Workflow
7
+
8
+ DEFAULT_META = {
9
+ :p_size => 0,
10
+ :item_label => Runo::I18n.n_('attachment', 'attachments', 1),
11
+ }
12
+
13
+ PERM = {
14
+ :create => 0b00000,
15
+ :read => 0b00000,
16
+ :update => 0b00000,
17
+ :delete => 0b00000,
18
+ }
19
+
20
+ def permit?(roles, action)
21
+ (action == :login) ||
22
+ (@sd[:parent] && @sd[:parent].permit?(action))
23
+ end
24
+
25
+ def _get(arg)
26
+ @sd.instance_eval {
27
+ if arg[:action] == :create || arg[:action] == :update
28
+ new_item = item_instance '_001'
29
+
30
+ item_outs = _g_default(arg) {|item, item_arg|
31
+ action = item[:id][Runo::REX::ID_NEW] ? :create : :delete
32
+ button_tmpl = my[:tmpl][:"submit_#{action}"] || <<_html.chomp
33
+ <input type="submit" name="@(short_name).action-#{action}" value="#{_ action.to_s}" />
34
+ _html
35
+ button = item.send(:_get_by_tmpl, {}, button_tmpl)
36
+ item_arg[:action] = :create if action == :create
37
+ item_tmpl = item[:tmpl][:index].sub(/[\w\W]*\$\(.*?\)/, "\\&#{button}")
38
+ item.send(:_get_by_tmpl, item_arg, item_tmpl)
39
+ }
40
+ tmpl = my[:tmpl][:index].gsub('$()', item_outs.join)
41
+ _get_by_tmpl({:p_action => arg[:p_action], :action => :update}, tmpl)
42
+ end
43
+ }
44
+ end
45
+
46
+ def _hide?(arg)
47
+ arg[:action] == :submit
48
+ end
49
+
50
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Workflow::Blog < Runo::Workflow
7
+
8
+ DEFAULT_META = {
9
+ :p_size => 10,
10
+ :conds => {:d => '999999', :p => 'last'},
11
+ :order => '-id',
12
+ :item_label => Runo::I18n.n_('entry', 'entries', 1),
13
+ }
14
+
15
+ DEFAULT_SUB_ITEMS = {
16
+ '_owner' => {:klass => 'meta-owner'},
17
+ '_group' => {:klass => 'meta-group'},
18
+ '_timestamp' => {:klass => 'meta-timestamp'},
19
+ }
20
+
21
+ PERM = {
22
+ :create => 0b11000,
23
+ :read => 0b11111,
24
+ :update => 0b10100,
25
+ :delete => 0b10100,
26
+ }
27
+
28
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Workflow::Contact < Runo::Workflow
7
+
8
+ DEFAULT_META = {
9
+ :p_size => 10,
10
+ :conds => {:p => 'last'},
11
+ :item_label => Runo::I18n.n_('item', 'items', 1),
12
+ }
13
+
14
+ DEFAULT_SUB_ITEMS = {}
15
+
16
+ PERM = {
17
+ :create => 0b00011,
18
+ :read => 0b11000,
19
+ :update => 0b00000,
20
+ :delete => 0b11000,
21
+ }
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Workflow::Forum < Runo::Workflow
7
+
8
+ DEFAULT_META = {
9
+ :conds => {:p => 'last'},
10
+ :p_size => 10,
11
+ :item_label => Runo::I18n.n_('post', 'posts', 1),
12
+ }
13
+
14
+ DEFAULT_SUB_ITEMS = {
15
+ '_owner' => {:klass => 'meta-owner'},
16
+ '_timestamp' => {:klass => 'meta-timestamp'},
17
+ }
18
+
19
+ PERM = {
20
+ :create => 0b11110,
21
+ :read => 0b11111,
22
+ :update => 0b11000,
23
+ :delete => 0b11000,
24
+ }
25
+
26
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Workflow::Register < Runo::Workflow
7
+
8
+ DEFAULT_META = {
9
+ :p_size => 0,
10
+ :conds => {:p => '1'},
11
+ :order => 'id',
12
+ :item_label => Runo::I18n.n_('item', 'items', 1),
13
+ }
14
+
15
+ DEFAULT_SUB_ITEMS = {
16
+ '_owner' => {:klass => 'meta-owner'},
17
+ '_timestamp' => {:klass => 'meta-timestamp'},
18
+ }
19
+
20
+ PERM = {
21
+ :create => 0b11001,
22
+ :read => 0b11100,
23
+ :update => 0b11100,
24
+ :delete => 0b11100,
25
+ }
26
+
27
+ def before_commit
28
+ @sd.send(:pending_items).each {|id, item|
29
+ if id =~ Runo::REX::ID_NEW
30
+ item.item('_owner').instance_variable_set(:@val, item.item('_id').val)
31
+ end
32
+ }
33
+ end
34
+
35
+ def next_action(base)
36
+ (Runo.client == 'nobody') ? :done : super
37
+ end
38
+
39
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Runo::Meta
7
+
8
+ def post(action, v = nil)
9
+ super
10
+ my[:parent][klass_id] = val if my[:parent]
11
+ self
12
+ end
13
+
14
+ private
15
+
16
+ def klass_id
17
+ self.class.to_s[/\w+$/].downcase.intern
18
+ end
19
+
20
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Meta::Group < Runo::Field
7
+
8
+ include Runo::Meta
9
+
10
+ private
11
+
12
+ def _post(action, v)
13
+ if action == :load
14
+ @val = val_cast v
15
+ end
16
+ nil
17
+ end
18
+
19
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Meta::Id < Runo::Field
7
+
8
+ # not include Runo::Meta as this is SHORT_ID, not a full id.
9
+
10
+ def initialize(meta = {})
11
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].first =~ /^\d+$/
12
+ super
13
+ end
14
+
15
+ def errors
16
+ if (my[:max].to_i > 0) && (val.size > my[:max])
17
+ [_('too long: %{max} characters maximum') % {:max => my[:max]}]
18
+ elsif (my[:min].to_i == 1) && val.empty?
19
+ [_ 'mandatory']
20
+ elsif (my[:min].to_i > 0) && (val.size < my[:min])
21
+ [_('too short: %{min} characters minimum') % {:min => my[:min]}]
22
+ elsif val !~ /\A#{Runo::REX::ID_SHORT}\z/
23
+ [_('malformatted id')]
24
+ elsif (
25
+ my[:parent] &&
26
+ my[:parent][:id] !~ /00000000_#{val}$/ &&
27
+ my[:parent][:parent].is_a?(Runo::Set::Dynamic) &&
28
+ my[:parent][:parent].item(val)
29
+ )
30
+ [_('duplicate id: %{id}') % {:id => val}]
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def _g_create(arg)
39
+ new_id? ? <<_html.chomp : _g_default(arg)
40
+ <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Runo::Field.h val}" size="#{my[:size]}" />#{_g_errors arg}</span>
41
+ _html
42
+ end
43
+ alias :_g_update :_g_create
44
+
45
+ def new_id?
46
+ find_ancestor {|i| i[:id] =~ Runo::REX::ID_NEW } ? true : false
47
+ end
48
+
49
+ def _post(action, v)
50
+ if action == :load || ([:create, :update].include?(action) && new_id?)
51
+ @val = val_cast v
52
+ end
53
+ end
54
+
55
+ def val_cast(v)
56
+ v.to_s
57
+ end
58
+
59
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Meta::Owner < Runo::Field
7
+
8
+ include Runo::Meta
9
+
10
+ private
11
+
12
+ def _post(action, v)
13
+ if action == :load
14
+ @val = val_cast v
15
+ elsif action == :create
16
+ @val = Runo.client
17
+ end
18
+ nil
19
+ end
20
+
21
+ end