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,20 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Bike::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 Bike::Meta::Group < Bike::Field
7
+
8
+ include Bike::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 Bike::Meta::Id < Bike::Field
7
+
8
+ # not include Bike::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#{Bike::REX::ID_SHORT}\z/
23
+ [_('malformatted id')]
24
+ elsif (
25
+ my[:parent] &&
26
+ my[:parent][:id] !~ /00000000_#{val}$/ &&
27
+ my[:parent][:parent].is_a?(Bike::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="#{Bike::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] =~ Bike::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 Bike::Meta::Owner < Bike::Field
7
+
8
+ include Bike::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 = Bike.client
17
+ end
18
+ nil
19
+ end
20
+
21
+ end
@@ -0,0 +1,118 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require 'time'
7
+
8
+ class Bike::Meta::Timestamp < Bike::Field
9
+
10
+ include Bike::Meta
11
+
12
+ REX_DATE = /\A(\d+).(\d+).(\d+)(?:[T\s](\d+):(\d+)(?::(\d+))?)?\z/
13
+
14
+ def initialize(meta = {})
15
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].find {|t| t =~ /^\d+$/ }
16
+ meta[:can_edit] = true if Array(meta[:tokens]).include? 'can_edit'
17
+ meta[:can_update] = true if Array(meta[:tokens]).include? 'can_update'
18
+ super
19
+ end
20
+
21
+ def errors
22
+ if @date_str.nil?
23
+ []
24
+ elsif @date_str =~ REX_DATE
25
+ (Time.local($1, $2, $3, $4, $5, $6) rescue nil) ? [] : ['out of range']
26
+ else
27
+ ['wrong format']
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def _g_default(arg)
34
+ _date val['published']
35
+ end
36
+ alias :_g_published :_g_default
37
+
38
+ def _g_rfc2822(arg)
39
+ val['published'].rfc2822
40
+ end
41
+
42
+ def _g_created(arg)
43
+ _date val['created']
44
+ end
45
+
46
+ def _g_updated(arg)
47
+ _date val['updated']
48
+ end
49
+
50
+ def _date(time)
51
+ time.is_a?(::Time) ? time.strftime(_('%Y-%m-%dT%H:%M:%S')) : 'n/a'
52
+ end
53
+
54
+ def _g_create(arg)
55
+ <<_html.chomp if my[:can_edit]
56
+ <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="" size="#{my[:size]}" /></span>
57
+ _html
58
+ end
59
+
60
+ def _g_update(arg)
61
+ if my[:can_edit]
62
+ v = @date_str
63
+ v ||= val['published'].is_a?(::Time) ? val['published'].strftime('%Y-%m-%d %H:%M:%S') : ''
64
+ <<_html.chomp
65
+ <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Bike::Field.h v}" size="#{my[:size]}" />#{_g_errors arg}</span>
66
+ _html
67
+ elsif my[:can_update] && !find_ancestor {|f| f[:id] =~ Bike::REX::ID_NEW }
68
+ <<_html
69
+ <span class="#{_g_class arg}">
70
+ <input type="checkbox" id="timestamp_#{my[:short_name]}" name="#{my[:short_name]}" value="true" />
71
+ <label for="timestamp_#{my[:short_name]}">#{_ 'update the timestamp'}</label>
72
+ #{_g_errors arg}</span>
73
+ _html
74
+ end
75
+ end
76
+
77
+ def _post(action, v)
78
+ case action
79
+ when :load
80
+ @val = val_cast v
81
+ when :create
82
+ now = Time.now
83
+ @val = {
84
+ 'created' => now,
85
+ 'updated' => now,
86
+ 'published' => now,
87
+ }
88
+ if my[:can_edit] && v['published'].is_a?(::Time)
89
+ @val['published'] = v['published']
90
+ else
91
+ nil # do not set @action
92
+ end
93
+ when :update
94
+ @val['updated'] = Time.now
95
+ if my[:can_edit] && v['published'].is_a?(::Time)
96
+ @val['published'] = v['published']
97
+ elsif my[:can_update] && v['published'] == :same_as_updated
98
+ @val['published'] = @val['updated']
99
+ else
100
+ nil # do not set @action
101
+ end
102
+ end
103
+ end
104
+
105
+ def val_cast(v)
106
+ if v.is_a? ::Hash
107
+ v
108
+ elsif v == 'true'
109
+ {'published' => :same_as_updated}
110
+ elsif v.is_a?(::String) && !v.empty?
111
+ @date_str = v
112
+ (v =~ REX_DATE && t = (Time.local($1, $2, $3, $4, $5, $6) rescue nil)) ? {'published' => t} : {}
113
+ else
114
+ {}
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Checkbox < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ if meta[:tokens]
10
+ meta[:options] ||= meta[:tokens] - ['mandatory']
11
+ meta[:options] = ['_on'] if meta[:options].empty?
12
+ meta[:mandatory] = meta[:tokens].include?('mandatory') && Array(meta[:options]).size > 1
13
+ end
14
+ if meta[:options].size == 1 && meta[:default] =~ /^(on|true|yes)$/i
15
+ meta[:default] = meta[:options].first
16
+ end
17
+ super
18
+ end
19
+
20
+ def errors
21
+ if val.empty?
22
+ my[:mandatory] ? [_('mandatory')] : []
23
+ else
24
+ (val - my[:options]).empty? ? [] : [_('no such option')]
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def _g_default(arg)
31
+ val.join ', '
32
+ end
33
+
34
+ def _g_update(arg)
35
+ if my[:options] == ['_on']
36
+ checked = (val.include? '_on') ? ' checked' : ''
37
+ <<_html
38
+ <span class="#{_g_class arg}">
39
+ <input type="hidden" name="#{my[:short_name]}[]" value="" />
40
+ <input type="checkbox" name="#{my[:short_name]}[]" value="_on" #{checked}/>
41
+ #{_g_errors arg}</span>
42
+ _html
43
+ else
44
+ options = my[:options].collect {|opt|
45
+ checked = (val.include? opt) ? ' checked' : ''
46
+ <<_html
47
+ <span class="item">
48
+ <input type="checkbox" id="checkbox_#{my[:short_name]}-#{opt}" name="#{my[:short_name]}[]" value="#{opt}"#{checked} />
49
+ <label for="checkbox_#{my[:short_name]}-#{opt}">#{opt}</label>
50
+ </span>
51
+ _html
52
+ }.join
53
+ <<_html
54
+ <span class="#{_g_class arg}">
55
+ <input type="hidden" name="#{my[:short_name]}[]" value="" />
56
+ #{options}#{_g_errors arg}</span>
57
+ _html
58
+ end
59
+ end
60
+ alias :_g_create :_g_update
61
+
62
+ def val_cast(v)
63
+ Array(v).collect {|i|
64
+ i.to_s unless i.to_s.empty?
65
+ }.compact
66
+ end
67
+
68
+ end
@@ -0,0 +1,144 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::File < Bike::Field
7
+
8
+ def initialize(meta = {})
9
+ meta[:options].collect! {|i| i.downcase } if meta[:options]
10
+ meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].first =~ /^\d+$/
11
+ super
12
+ end
13
+
14
+ def meta_path
15
+ my[:full_name].gsub('-', '/')
16
+ end
17
+
18
+ def meta_tmp_path
19
+ "#{Bike.base[:path]}/#{Bike.base[:tid]}/#{my[:short_name].gsub('-', '/')}" if Bike.base
20
+ end
21
+
22
+ def meta_persistent_sd
23
+ find_ancestor {|f| f.is_a?(Bike::Set::Dynamic) && f.storage.persistent? }
24
+ end
25
+
26
+ def meta_persistent_name
27
+ f = my[:parent]
28
+ ps = my[:persistent_sd]
29
+ name = my[:id]
30
+ until f == ps
31
+ name = "#{f[:id]}-#{name}"
32
+ f = f[:parent]
33
+ end
34
+ name
35
+ end
36
+
37
+ def body
38
+ raise Bike::Error::Forbidden unless permit? :read
39
+
40
+ if ps = my[:persistent_sd]
41
+ @body ||= ps.storage.val my[:persistent_name]
42
+ end
43
+ @body
44
+ end
45
+
46
+ def errors
47
+ if (
48
+ val['basename'] &&
49
+ my[:options].is_a?(::Array) &&
50
+ !my[:options].empty? &&
51
+ !my[:options].include?(val['basename'].to_s[/\.([\w\.]+)$/i, 1].downcase)
52
+ )
53
+ [_('wrong file type: should be %{types}') % {:types => my[:options].join('/')}]
54
+ elsif (my[:max].to_i > 0) && (val['size'].to_i > my[:max])
55
+ [_('too large: %{max} bytes maximum') % {:max => my[:max]}]
56
+ elsif (my[:min].to_i == 1) && val['size'].to_i < 1
57
+ [_('mandatory')]
58
+ elsif (my[:min].to_i > 0) && (val['size'].to_i < my[:min])
59
+ [_('too small: %{min} bytes minimum') % {:min => my[:min]}]
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ def commit(type = :temp)
66
+ if type == :temp && @action == :delete
67
+ @val = {}
68
+ @body = nil
69
+ elsif type == :persistent && ps = my[:persistent_sd]
70
+ case @action
71
+ when :create, :update, nil
72
+ ps.storage.store(
73
+ my[:persistent_name],
74
+ @body,
75
+ val['basename'][/\.([\w\.]+)$/, 1] || 'bin'
76
+ ) if @body && valid?
77
+ when :delete
78
+ ps.storage.delete my[:persistent_name]
79
+ end
80
+ end
81
+ super
82
+ end
83
+
84
+ private
85
+
86
+ def _g_default(arg = {})
87
+ path = _path arg[:action]
88
+ basename = Bike::Field.h val['basename']
89
+ type = Bike::Field.h val['type']
90
+ <<_html.chomp unless val.empty?
91
+ <a href="#{path}/#{basename}">#{basename} (#{val['size']} bytes)</a>
92
+ _html
93
+ end
94
+
95
+ def _g_update(arg)
96
+ hidden = <<_html if my[:min].to_i > 0 && val.empty?
97
+ <input type="hidden" name="#{my[:short_name]}" value="" />
98
+ _html
99
+ if (
100
+ !val.empty? &&
101
+ my[:min].to_i == 0 &&
102
+ my[:parent].is_a?(Bike::Set::Static) &&
103
+ my[:parent][:item].find {|id, meta| id != my[:id] && meta[:klass] !~ /^meta-/ }
104
+ )
105
+ delete = <<_html
106
+ <input type="submit" name="#{my[:short_name]}.action-delete" value="#{_ 'delete'}" />
107
+ _html
108
+ end
109
+ update = <<_html
110
+ <input type="file" name="#{my[:short_name]}" size="#{my[:size]}" class="file" />
111
+ _html
112
+ <<_html
113
+ <span class="#{_g_class arg}">
114
+ #{_g_default arg}
115
+ #{hidden}#{update}#{delete}#{_g_errors arg}</span>
116
+ _html
117
+ end
118
+ alias :_g_create :_g_update
119
+
120
+ def _path(action)
121
+ Bike.uri.to_s + ([:read, nil].include?(action) ? my[:path] : my[:tmp_path])
122
+ end
123
+
124
+ def val_cast(v)
125
+ if v.is_a?(::Hash) && v[:tempfile]
126
+ v[:tempfile].rewind
127
+ @body = v[:tempfile].read
128
+ {
129
+ 'basename' => File.basename(v[:filename]),
130
+ 'type' => v[:type] || 'application/octet-stream',
131
+ 'size' => @body.respond_to?(:bytesize) ? @body.bytesize : @body.size,
132
+ }
133
+ elsif v.is_a?(::Hash) && v['basename']
134
+ {
135
+ 'basename' => v['basename'].to_s,
136
+ 'type' => v['type'].to_s,
137
+ 'size' => v['size'].to_i,
138
+ }
139
+ else
140
+ {}
141
+ end
142
+ end
143
+
144
+ end