ramaze 2011.07.25 → 2011.10.23
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.mailmap +3 -2
- data/.travis.yml +17 -0
- data/.yardopts +13 -0
- data/README.md +95 -352
- data/examples/app/blog/app.rb +25 -64
- data/examples/app/blog/config.ru +11 -9
- data/examples/app/blog/controller/init.rb +29 -86
- data/examples/app/blog/controller/posts.rb +232 -0
- data/examples/app/blog/controller/users.rb +160 -0
- data/examples/app/blog/layout/default.xhtml +61 -0
- data/examples/app/blog/migrations/01_create_schema.rb +50 -0
- data/examples/app/blog/model/comment.rb +41 -54
- data/examples/app/blog/model/init.rb +41 -13
- data/examples/app/blog/model/post.rb +35 -0
- data/examples/app/blog/model/user.rb +105 -0
- data/examples/app/blog/public/.htaccess +24 -0
- data/examples/app/blog/public/css/grid.css +107 -0
- data/examples/app/blog/public/css/layout.css +203 -0
- data/examples/app/blog/public/css/reset.css +123 -0
- data/examples/app/blog/public/css/text.css +109 -0
- data/examples/app/blog/public/dispatch.fcgi +11 -0
- data/examples/app/blog/public/favicon.ico +0 -0
- data/examples/app/blog/public/images/bg.png +0 -0
- data/examples/app/blog/start.rb +18 -3
- data/examples/app/blog/view/feed.xhtml +23 -0
- data/examples/app/blog/view/form.xhtml +11 -0
- data/examples/app/blog/view/index.xhtml +44 -0
- data/examples/app/blog/view/users/form.xhtml +12 -0
- data/examples/app/blog/view/users/index.xhtml +30 -0
- data/examples/app/blog/view/users/login.xhtml +8 -0
- data/examples/app/blog/view/view.xhtml +68 -0
- data/{doc → guide}/AUTHORS +5 -3
- data/{doc → guide}/CHANGELOG +428 -0
- data/{doc/GPL → guide/GPL_LICENSE} +0 -0
- data/{doc/COPYING → guide/RUBY_LICENSE} +3 -6
- data/guide/_static/logo.png +0 -0
- data/guide/_static/logo.svg +49 -0
- data/guide/_static/ramaze_console.png +0 -0
- data/guide/css/common.css +20 -0
- data/guide/general/cache.md +167 -0
- data/guide/general/configuration.md +168 -0
- data/guide/general/contributing.md +108 -0
- data/guide/general/controllers.md +115 -0
- data/guide/general/helpers.md +76 -0
- data/guide/general/installation.md +58 -0
- data/guide/general/logging.md +99 -0
- data/guide/general/middlewares.md +100 -0
- data/guide/general/models.md +78 -0
- data/guide/general/principles.md +53 -0
- data/guide/general/ramaze_command.md +155 -0
- data/guide/general/routes.md +81 -0
- data/guide/general/sessions.md +140 -0
- data/guide/general/special_thanks.md +67 -0
- data/guide/general/testing.md +61 -0
- data/guide/general/views.md +322 -0
- data/guide/tutorials/introduction.md +259 -0
- data/lib/proto/config.ru +1 -1
- data/lib/proto/public/favicon.ico +0 -0
- data/lib/proto/view/index.xhtml +7 -7
- data/lib/ramaze.rb +4 -4
- data/lib/ramaze/app.rb +11 -11
- data/lib/ramaze/app_graph.rb +2 -4
- data/lib/ramaze/bin/console.rb +3 -3
- data/lib/ramaze/bin/create.rb +2 -2
- data/lib/ramaze/bin/restart.rb +4 -4
- data/lib/ramaze/bin/runner.rb +5 -5
- data/lib/ramaze/bin/start.rb +19 -4
- data/lib/ramaze/bin/status.rb +3 -3
- data/lib/ramaze/bin/stop.rb +3 -3
- data/lib/ramaze/cache.rb +1 -0
- data/lib/ramaze/cache/lru.rb +8 -4
- data/lib/ramaze/cache/memcache.rb +32 -13
- data/lib/ramaze/cache/redis.rb +164 -0
- data/lib/ramaze/cache/sequel.rb +43 -28
- data/lib/ramaze/controller.rb +1 -2
- data/lib/ramaze/dependencies.rb +40 -3
- data/lib/ramaze/helper/bench.rb +26 -16
- data/lib/ramaze/helper/blue_form.rb +46 -73
- data/lib/ramaze/helper/cache.rb +10 -6
- data/lib/ramaze/helper/csrf.rb +35 -39
- data/lib/ramaze/helper/disqus.rb +5 -4
- data/lib/ramaze/helper/email.rb +35 -24
- data/lib/ramaze/helper/erector.rb +9 -13
- data/lib/ramaze/helper/flash.rb +7 -9
- data/lib/ramaze/helper/formatting.rb +194 -179
- data/lib/ramaze/helper/gravatar.rb +4 -8
- data/lib/ramaze/helper/identity.rb +3 -3
- data/lib/ramaze/helper/layout.rb +23 -8
- data/lib/ramaze/helper/markaby.rb +1 -1
- data/lib/ramaze/helper/paginate.rb +46 -39
- data/lib/ramaze/helper/request_accessor.rb +3 -1
- data/lib/ramaze/helper/simple_captcha.rb +18 -17
- data/lib/ramaze/helper/stack.rb +1 -1
- data/lib/ramaze/helper/tagz.rb +4 -2
- data/lib/ramaze/helper/upload.rb +523 -0
- data/lib/ramaze/helper/user.rb +4 -8
- data/lib/ramaze/helper/xhtml.rb +11 -15
- data/lib/ramaze/log.rb +9 -6
- data/lib/ramaze/log/rotatinginformer.rb +62 -27
- data/lib/ramaze/log/syslog.rb +20 -15
- data/lib/ramaze/log/xosd.rb +2 -1
- data/lib/ramaze/reloader.rb +2 -0
- data/lib/ramaze/request.rb +11 -10
- data/lib/ramaze/setup.rb +23 -6
- data/lib/ramaze/snippets/array/put_within.rb +3 -9
- data/lib/ramaze/snippets/binding/locals.rb +5 -10
- data/lib/ramaze/snippets/fiber.rb +1 -23
- data/lib/ramaze/snippets/kernel/pretty_inspect.rb +3 -6
- data/lib/ramaze/snippets/numeric/filesize_format.rb +3 -5
- data/lib/ramaze/snippets/numeric/time.rb +3 -7
- data/lib/ramaze/snippets/object/__dir__.rb +3 -7
- data/lib/ramaze/snippets/object/instance_variable_defined.rb +3 -6
- data/lib/ramaze/snippets/object/pretty.rb +3 -7
- data/lib/ramaze/snippets/object/scope.rb +7 -9
- data/lib/ramaze/snippets/proc/locals.rb +12 -12
- data/lib/ramaze/snippets/ramaze/acquire.rb +15 -14
- data/lib/ramaze/snippets/ramaze/deprecated.rb +1 -1
- data/lib/ramaze/snippets/ramaze/fiber.rb +1 -1
- data/lib/ramaze/snippets/ramaze/lru_hash.rb +2 -3
- data/lib/ramaze/snippets/ramaze/struct.rb +2 -4
- data/lib/ramaze/snippets/string/camel_case.rb +8 -10
- data/lib/ramaze/snippets/string/color.rb +3 -4
- data/lib/ramaze/snippets/string/end_with.rb +3 -6
- data/lib/ramaze/snippets/string/esc.rb +3 -8
- data/lib/ramaze/snippets/string/ord.rb +3 -8
- data/lib/ramaze/snippets/string/snake_case.rb +6 -9
- data/lib/ramaze/snippets/string/start_with.rb +3 -8
- data/lib/ramaze/snippets/string/unindent.rb +3 -6
- data/lib/ramaze/snippets/thread/into.rb +1 -3
- data/lib/ramaze/spec.rb +2 -31
- data/lib/ramaze/spec/bacon.rb +18 -2
- data/lib/ramaze/version.rb +1 -1
- data/lib/ramaze/view.rb +1 -1
- data/ramaze.gemspec +1 -1
- data/spec/helper.rb +2 -1
- data/spec/ramaze/bin/start.rb +16 -20
- data/spec/ramaze/cache/localmemcache.rb +4 -7
- data/spec/ramaze/cache/memcache.rb +3 -1
- data/spec/ramaze/cache/redis.rb +62 -0
- data/spec/ramaze/helper/blue_form.rb +33 -4
- data/spec/ramaze/helper/layout.rb +40 -7
- data/spec/ramaze/helper/upload.rb +149 -0
- data/spec/ramaze/helper/uploads/text_1.txt +1 -0
- data/spec/ramaze/helper/uploads/text_2.txt +1 -0
- data/spec/ramaze/log/growl.rb +4 -6
- data/spec/ramaze/log/syslog.rb +6 -0
- data/spec/ramaze/view/lokar.rb +5 -0
- data/spec/ramaze/view/nagoro.rb +5 -0
- data/tasks/authors.rake +1 -1
- data/tasks/bacon.rake +14 -5
- data/tasks/changelog.rake +1 -1
- data/tasks/yard.rake +12 -4
- metadata +277 -239
- data/doc/LEGAL +0 -26
- data/examples/app/blog/README +0 -3
- data/examples/app/blog/controller/comment.rb +0 -45
- data/examples/app/blog/controller/entry.rb +0 -85
- data/examples/app/blog/controller/main.rb +0 -20
- data/examples/app/blog/controller/tag.rb +0 -9
- data/examples/app/blog/layout/default.nag +0 -31
- data/examples/app/blog/model/entry.rb +0 -89
- data/examples/app/blog/model/tag.rb +0 -36
- data/examples/app/blog/public/css/screen.css +0 -273
- data/examples/app/blog/spec/blog.rb +0 -87
- data/examples/app/blog/view/comment/form.nag +0 -10
- data/examples/app/blog/view/comment/show.nag +0 -16
- data/examples/app/blog/view/entry/edit.nag +0 -14
- data/examples/app/blog/view/entry/feed.atom.nag +0 -8
- data/examples/app/blog/view/entry/feed.rss.nag +0 -7
- data/examples/app/blog/view/entry/index.nag +0 -7
- data/examples/app/blog/view/entry/new.nag +0 -13
- data/examples/app/blog/view/entry/show.nag +0 -36
- data/examples/app/blog/view/feed.atom.nag +0 -18
- data/examples/app/blog/view/feed.rss.nag +0 -25
- data/examples/app/blog/view/index.nag +0 -6
- data/examples/app/blog/view/tag/index.nag +0 -5
- data/lib/proto/public/ramaze.png +0 -0
- data/lib/ramaze/rest.rb +0 -36
- data/spec/ramaze/rest.rb +0 -28
- data/tasks/rcov.rake +0 -22
@@ -1,6 +1,5 @@
|
|
1
1
|
module Ramaze
|
2
2
|
module Helper
|
3
|
-
|
4
3
|
# Helps you building Gravatar URIs from an email address.
|
5
4
|
#
|
6
5
|
# For more information about gravatars, please see: http://gravatar.com
|
@@ -12,11 +11,9 @@ module Ramaze
|
|
12
11
|
# It might not know about all the secret parameters (like 'force'), if you
|
13
12
|
# find out more of these please consider contributing a patch.
|
14
13
|
module Gravatar
|
15
|
-
|
16
14
|
# API to build gravatar URIs from an email address (or any other String).
|
17
15
|
#
|
18
16
|
# @example Simple usage
|
19
|
-
#
|
20
17
|
# class Gravatars < Ramaze::Controller
|
21
18
|
# helper :gravatar
|
22
19
|
#
|
@@ -55,11 +52,10 @@ module Ramaze
|
|
55
52
|
# identicons or the like
|
56
53
|
#
|
57
54
|
# @param [#to_str] email
|
58
|
-
#
|
59
55
|
# @return [URI]
|
60
|
-
#
|
61
56
|
# @see http://en.gravatar.com/site/implement/url
|
62
57
|
# @author manveru
|
58
|
+
#
|
63
59
|
def gravatar(email, opts = {})
|
64
60
|
uri = URI("http://www.gravatar.com/")
|
65
61
|
ext = opts[:ext]
|
@@ -74,6 +70,6 @@ module Ramaze
|
|
74
70
|
uri.query = Rack::Utils.build_query(query) if query.any?
|
75
71
|
uri
|
76
72
|
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
73
|
+
end # Gravatar
|
74
|
+
end # Helper
|
75
|
+
end # Ramaze
|
data/lib/ramaze/helper/layout.rb
CHANGED
@@ -11,22 +11,22 @@ module Ramaze
|
|
11
11
|
# specify a number of layouts and the methods for which these layouts should
|
12
12
|
# be used.
|
13
13
|
#
|
14
|
-
#
|
14
|
+
# ## Examples
|
15
15
|
#
|
16
16
|
# The most basic example is simply setting a layout as you would do with the
|
17
17
|
# layout() method:
|
18
18
|
#
|
19
|
-
#
|
19
|
+
# set_layout 'default'
|
20
20
|
#
|
21
21
|
# This of course is very boring, time to add some more spices to our code:
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# set_layout 'default' => [:index]
|
24
24
|
#
|
25
25
|
# Woah! What just happened? It's quite easy actually, we merely defined that
|
26
26
|
# the layout called "default" should be used for the index method *only*.
|
27
27
|
# Pretty sweet huh? It gets even better:
|
28
28
|
#
|
29
|
-
#
|
29
|
+
# set_layout 'default' => [:index, :edit], 'alternative' => [:add, :process]
|
30
30
|
#
|
31
31
|
# A few things changed. First of all there are now two key/value groups.
|
32
32
|
# Each group defines a layout (the key) and a set of methods (the value) for
|
@@ -49,7 +49,7 @@ module Ramaze
|
|
49
49
|
# Extends the class that included this module so that the methods that
|
50
50
|
# this helper provides can be called outside of instance of class methods.
|
51
51
|
#
|
52
|
-
# @param
|
52
|
+
# @param [Object] into The class that included this module.
|
53
53
|
# @author Michael Fellinger
|
54
54
|
# @author Pistos
|
55
55
|
#
|
@@ -85,8 +85,10 @@ module Ramaze
|
|
85
85
|
# @author Michael Fellinger
|
86
86
|
# @author Pistos
|
87
87
|
# @since 2011-04-07
|
88
|
+
#
|
88
89
|
def set_layout(hash_or_layout)
|
89
|
-
@_ramaze_layouts
|
90
|
+
@_ramaze_layouts ||= {}
|
91
|
+
@_ramaze_old_layout ||= trait[:layout]
|
90
92
|
|
91
93
|
# Extract the layout to use
|
92
94
|
if hash_or_layout.respond_to?(:to_hash)
|
@@ -97,9 +99,22 @@ module Ramaze
|
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
100
|
-
layout
|
102
|
+
layout do |path, wish|
|
103
|
+
path = path.to_s
|
104
|
+
|
105
|
+
if @_ramaze_layouts.key?(path)
|
106
|
+
use_layout = @_ramaze_layouts[path.to_s]
|
107
|
+
# Use the old layout
|
108
|
+
elsif @_ramaze_old_layout.respond_to?(:call)
|
109
|
+
use_layout = @_ramaze_old_layout.call(path, wish)
|
110
|
+
else
|
111
|
+
use_layout = @_ramaze_old_layout
|
112
|
+
end
|
113
|
+
|
114
|
+
use_layout
|
115
|
+
end
|
101
116
|
else
|
102
|
-
layout{|path| hash_or_layout }
|
117
|
+
layout { |path| hash_or_layout }
|
103
118
|
end
|
104
119
|
end
|
105
120
|
|
@@ -6,7 +6,7 @@ module Ramaze
|
|
6
6
|
# Allows you to use some shortcuts for markaby in your Controller.
|
7
7
|
module Markaby
|
8
8
|
##
|
9
|
-
#
|
9
|
+
# Use this inside your controller to directly build Markaby
|
10
10
|
# Refer to the Markaby-documentation and testsuite for more examples.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -4,8 +4,8 @@ module Ramaze
|
|
4
4
|
module Helper
|
5
5
|
# Helper for pagination and pagination-navigation.
|
6
6
|
#
|
7
|
-
# See
|
8
|
-
#
|
7
|
+
# See {Ramaze::Helper::Paginate#paginate} for more information.
|
8
|
+
#
|
9
9
|
module Paginate
|
10
10
|
include Traited
|
11
11
|
|
@@ -26,31 +26,36 @@ module Ramaze
|
|
26
26
|
# The examples below are meant to be used within your controller or view.
|
27
27
|
#
|
28
28
|
# Usage with Array:
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
29
|
+
#
|
30
|
+
# data = (1..100).to_a
|
31
|
+
# @pager = paginate(data, :limit => 30, :page => 2)
|
32
|
+
# @pager.navigation
|
33
|
+
# @pager.each{|e| puts(e) }
|
33
34
|
#
|
34
35
|
# Usage with Sequel:
|
35
|
-
# data = Article.filter(:public => true)
|
36
|
-
# @pager = paginate(data, :limit => 5)
|
37
|
-
# @pager.navigation
|
38
|
-
# @pager.each{|e| puts(e)
|
39
36
|
#
|
40
|
-
#
|
41
|
-
#
|
37
|
+
# data = Article.filter(:public => true)
|
38
|
+
# @pager = paginate(data, :limit => 5)
|
39
|
+
# @pager.navigation
|
40
|
+
# @pager.each{|e| puts(e)
|
41
|
+
#
|
42
|
+
# Note that you must first extend Sequel with the pagination extension"
|
43
|
+
#
|
42
44
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
45
|
+
# Sequel.extension :pagination
|
46
|
+
#
|
47
|
+
# @param [Sequel::Dataset|Array] dataset May be a Sequel dataset or an
|
48
|
+
# Array
|
49
|
+
# @param [Hash] options A hash containing custom options that takes
|
50
|
+
# precedence to ``trait[:paginate].
|
51
|
+
# @option options [Fixnum] :limit The number of elements used when you
|
52
|
+
# call #each on the paginator
|
53
|
+
# @option options [String] :var The variable name being used in the
|
54
|
+
# request, this is helpful if you want to use two or more independent
|
55
|
+
# paginations on the same page.
|
56
|
+
# @option options [Fixnum] :page The page you are currently on, if not
|
57
|
+
# given it will be retrieved from current request variables. Defaults to
|
58
|
+
# 1 if neither exists.
|
54
59
|
#
|
55
60
|
def paginate(dataset, options = {})
|
56
61
|
options = ancestral_trait[:paginate].merge(options)
|
@@ -80,24 +85,26 @@ module Ramaze
|
|
80
85
|
# with CSS.
|
81
86
|
#
|
82
87
|
# Output with 5 elements, page 1, limit 3:
|
83
|
-
#
|
84
|
-
# <
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
88
|
+
#
|
89
|
+
# <div class="pager">
|
90
|
+
# <span class="first grey"><<</span>
|
91
|
+
# <span class="previous grey"><</span>
|
92
|
+
# <a class="current" href="/index?pager=1">1</a>
|
93
|
+
# <a href="/index?pager=2">2</a>
|
94
|
+
# <a class="next" href="/index?pager=2">></a>
|
95
|
+
# <a class="last" href="/index?pager=2">>></a>
|
96
|
+
# </div>
|
91
97
|
#
|
92
98
|
# Output with 5 elements, page 2, limit 3:
|
93
|
-
#
|
94
|
-
# <
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
99
|
+
#
|
100
|
+
# <div class="pager" />
|
101
|
+
# <a class="first" href="/index?user_page=1"><<</a>
|
102
|
+
# <a class="previous" href="/index?user_page=1"><</a>
|
103
|
+
# <a href="/index?user_page=1">1</a>
|
104
|
+
# <a class="current" href="/index?user_page=2">2</a>
|
105
|
+
# <span class="next grey">></span>
|
106
|
+
# <span class="last grey">>></span>
|
107
|
+
# </div>
|
101
108
|
#
|
102
109
|
def navigation(limit = 8)
|
103
110
|
g = Ramaze::Gestalt.new
|
@@ -2,7 +2,9 @@ module Ramaze
|
|
2
2
|
module Helper
|
3
3
|
module RequestAccessor
|
4
4
|
classes = [Rack::Request, Innate::Request, Ramaze::Request]
|
5
|
-
methods = classes.map{|klass|
|
5
|
+
methods = classes.map { |klass|
|
6
|
+
klass.instance_methods(false)
|
7
|
+
}.flatten.uniq
|
6
8
|
|
7
9
|
methods.each do |method|
|
8
10
|
if method =~ /=/
|
@@ -6,25 +6,26 @@ module Ramaze
|
|
6
6
|
#
|
7
7
|
# Usage (trait is optional):
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
9
|
+
# class RegisterController < Ramaze::Controller
|
10
|
+
# trait :captcha => lambda{
|
11
|
+
# ["the answer to everything", "42"]
|
12
|
+
# }
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
14
|
+
# def index
|
15
|
+
# %(
|
16
|
+
# <form action="#{r(:answer}">
|
17
|
+
# What is #{simple_captcha}?
|
18
|
+
# <input type="text" name="answer" />"
|
19
|
+
# <input type="submit" />
|
20
|
+
# </form>
|
21
|
+
# ).strip
|
22
|
+
# end
|
23
23
|
#
|
24
|
-
#
|
25
|
-
#
|
24
|
+
# def answer
|
25
|
+
# check_captcha(request[:answer])
|
26
|
+
# end
|
26
27
|
# end
|
27
|
-
#
|
28
|
+
#
|
28
29
|
module SimpleCaptcha
|
29
30
|
include Ramaze::Traited
|
30
31
|
|
@@ -56,5 +57,5 @@ module Ramaze
|
|
56
57
|
answer.to_s.strip == captcha[:answer].to_s
|
57
58
|
end
|
58
59
|
end # SimpleCaptcha
|
59
|
-
end # Helper
|
60
|
+
end # Helper
|
60
61
|
end # Ramaze
|
data/lib/ramaze/helper/stack.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
module Ramaze
|
5
5
|
module Helper
|
6
6
|
##
|
7
|
-
#
|
7
|
+
# Provides an call/answer mechanism, this is useful for example in a
|
8
8
|
# login-system.
|
9
9
|
#
|
10
10
|
# It is basically good to redirect temporarly somewhere else without
|
data/lib/ramaze/helper/tagz.rb
CHANGED
@@ -9,9 +9,11 @@ module Ramaze
|
|
9
9
|
# Allows you to use some shortcuts for Tagz in your Controller.
|
10
10
|
# use this inside your controller to directly build Tagz
|
11
11
|
# Refer to the Tagz-documentation and testsuite for more examples.
|
12
|
+
#
|
12
13
|
# Usage:
|
13
|
-
#
|
14
|
-
#
|
14
|
+
#
|
15
|
+
# tagz { h1_{ "Apples & Oranges" } } #=> "<h1>Apples & Oranges</h1>"
|
16
|
+
# tagz { h1_(:class => 'fruits&floots'){ 'Apples' } }
|
15
17
|
#
|
16
18
|
Tagz = ::Tagz
|
17
19
|
end # Helper
|
@@ -0,0 +1,523 @@
|
|
1
|
+
module Ramaze
|
2
|
+
module Helper
|
3
|
+
##
|
4
|
+
# Helper module for handling file uploads. File uploads are mostly handled
|
5
|
+
# by Rack, but this helper adds some conveniance methods for handling
|
6
|
+
# and saving the uploaded files.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class MyController < Ramaze::Controller
|
10
|
+
# # Use upload helper
|
11
|
+
# helper :upload
|
12
|
+
#
|
13
|
+
# # This action will handle *all* uploaded files
|
14
|
+
# def handleupload1
|
15
|
+
# # Iterate over uploaded files and save them in the
|
16
|
+
# # '/uploads/myapp' directory
|
17
|
+
# get_uploaded_files.each_pair do |k, v|
|
18
|
+
# v.save(
|
19
|
+
# File.join('/uploads/myapp', v.filename),
|
20
|
+
# :allow_overwrite => true
|
21
|
+
# )
|
22
|
+
#
|
23
|
+
# if v.saved?
|
24
|
+
# Ramaze::Log.info(
|
25
|
+
# "Saved uploaded file named #{k} to #{v.path}."
|
26
|
+
# )
|
27
|
+
# else
|
28
|
+
# Ramaze::Log.warn("Failed to save file named #{k}.")
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # This action will handle uploaded files beginning with 'up'
|
34
|
+
# def handleupload2
|
35
|
+
# # Iterate over uploaded files and save them in the
|
36
|
+
# # '/uploads/myapp' directory
|
37
|
+
# get_uploaded_files(/^up.*/).each_pair do |k, v|
|
38
|
+
# v.save(
|
39
|
+
# File.join('/uploads/myapp', v.filename),
|
40
|
+
# :allow_overwrite => true
|
41
|
+
# )
|
42
|
+
#
|
43
|
+
# if v.saved?
|
44
|
+
# Ramaze::Log.info(
|
45
|
+
# "Saved uploaded file named #{k} to #{v.path}."
|
46
|
+
# )
|
47
|
+
# else
|
48
|
+
# Ramaze::Log.warn("Failed to save file named #{k}.")
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @author Lars Olsson
|
55
|
+
# @since 04-08-2011
|
56
|
+
#
|
57
|
+
module Upload
|
58
|
+
include Ramaze::Traited
|
59
|
+
|
60
|
+
##
|
61
|
+
# This method will iterate through all request parameters and convert
|
62
|
+
# those parameters which represents uploaded files to
|
63
|
+
# Ramaze::Helper::Upload::UploadedFile objects. The matched parameters
|
64
|
+
# will then be removed from the request parameter hash.
|
65
|
+
#
|
66
|
+
# Use this method if you want to decide whether to handle file uploads
|
67
|
+
# in your action at runtime. For automatic handling, use
|
68
|
+
# Ramaze::Helper::Upload::ClassMethods#handle_all_uploads or
|
69
|
+
# Ramaze::Helper::Upload::ClassMethods#handle_uploads_for instead.
|
70
|
+
#
|
71
|
+
# @author Lars Olsson
|
72
|
+
# @since 04-08-2011
|
73
|
+
# @param [Regexp] pattern If set, only those request parameters which
|
74
|
+
# has a name matching the Regexp will be checked for file uploads.
|
75
|
+
# @return [Array] The uploaded files.
|
76
|
+
# @see Ramaze::Helper::Upload::ClassMethods#handle_all_uploads
|
77
|
+
# @see Ramaze::Helper::Upload::ClassMethods#handle_uploads_for
|
78
|
+
#
|
79
|
+
def get_uploaded_files(pattern = nil)
|
80
|
+
uploaded_files = {}
|
81
|
+
|
82
|
+
# Iterate over all request parameters
|
83
|
+
request.params.each_pair do |k, v|
|
84
|
+
# If we use a pattern, check that it matches
|
85
|
+
if pattern.nil? or pattern =~ k
|
86
|
+
# Rack supports request parameters with either a single value or
|
87
|
+
# an array of values. To support both, we need to check if the
|
88
|
+
# current parameter is an array or not.
|
89
|
+
if v.is_a?(Array)
|
90
|
+
# Got an array. Iterate through it and check for uploaded files
|
91
|
+
file_indices = []
|
92
|
+
|
93
|
+
v.each_with_index do |elem, idx|
|
94
|
+
file_indices.push(idx) if is_uploaded_file?(elem)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Convert found uploaded files to
|
98
|
+
# Ramaze::Helper::Upload::UploadedFile objects
|
99
|
+
file_elems = []
|
100
|
+
|
101
|
+
file_indices.each do |fi|
|
102
|
+
file_elems << Ramaze::Helper::Upload::UploadedFile.new(
|
103
|
+
v[fi][:filename],
|
104
|
+
v[fi][:type],
|
105
|
+
v[fi][:tempfile],
|
106
|
+
ancestral_trait[:upload_options] ||
|
107
|
+
Ramaze::Helper::Upload::ClassMethods.trait[
|
108
|
+
:default_upload_options
|
109
|
+
]
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Remove uploaded files from current request param
|
114
|
+
file_indices.reverse_each do |fi|
|
115
|
+
v.delete_at(fi)
|
116
|
+
end
|
117
|
+
|
118
|
+
# If the request parameter contained at least one file upload,
|
119
|
+
# add upload(s) to the list of uploaded files
|
120
|
+
uploaded_files[k] = file_elems unless file_elems.empty?
|
121
|
+
|
122
|
+
# Delete parameter from request parameter array if it doesn't
|
123
|
+
# contain any other elements.
|
124
|
+
request.params.delete(k) if v.empty?
|
125
|
+
else
|
126
|
+
# Got a single value. Check if it is an uploaded file
|
127
|
+
if is_uploaded_file?(v)
|
128
|
+
# The current parameter represents an uploaded file.
|
129
|
+
# Convert the parameter to a
|
130
|
+
# Ramaze::Helper::Upload::UploadedFile object
|
131
|
+
uploaded_files[k] = Ramaze::Helper::Upload::UploadedFile.new(
|
132
|
+
v[:filename],
|
133
|
+
v[:type],
|
134
|
+
v[:tempfile],
|
135
|
+
ancestral_trait[:upload_options] ||
|
136
|
+
Ramaze::Helper::Upload::ClassMethods.trait[
|
137
|
+
:default_upload_options
|
138
|
+
]
|
139
|
+
)
|
140
|
+
|
141
|
+
# Delete parameter from request parameter array
|
142
|
+
request.params.delete(k)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# If at least one file upload matched, override the uploaded_files
|
149
|
+
# method with a singleton method that returns the list of uploaded
|
150
|
+
# files. Doing things this way allows us to store the list of uploaded
|
151
|
+
# files without using an instance variable.
|
152
|
+
unless uploaded_files.empty?
|
153
|
+
@_ramaze_uploaded_files = uploaded_files
|
154
|
+
|
155
|
+
# Save uploaded files if autosave is set to true
|
156
|
+
if ancestral_trait[:upload_options] and
|
157
|
+
ancestral_trait[:upload_options][:autosave]
|
158
|
+
uploaded_files().each_value do |uf|
|
159
|
+
uf.save
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# The () is required, otherwise the name would collide with the variable
|
165
|
+
# "uploaded_files".
|
166
|
+
return uploaded_files()
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Adds some class method to the controller whenever the helper
|
171
|
+
# is included.
|
172
|
+
#
|
173
|
+
def self.included(mod)
|
174
|
+
mod.extend(ClassMethods)
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Returns list of currently handled file uploads.
|
179
|
+
#
|
180
|
+
# Both single and array parameters are supported. If you give
|
181
|
+
# your file upload fields the same name (for instance upload[]) Rack will
|
182
|
+
# merge them into a single parameter. The upload helper will keep this
|
183
|
+
# structure so that whenever the request parameter contains an array, the
|
184
|
+
# uploaded_files method will also return an array of
|
185
|
+
# Ramaze::Helper::Upload::UploadedFile objects for the same key.
|
186
|
+
#
|
187
|
+
# @return [Hash] Currently uploaded files. The keys in the hash
|
188
|
+
# corresponds to the names of the request parameters that contained file
|
189
|
+
# uploads and the values consist of Ramaze::Helper::Upload::UploadedFile
|
190
|
+
# objects.
|
191
|
+
#
|
192
|
+
def uploaded_files
|
193
|
+
return @_ramaze_uploaded_files || {}
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
# Returns whether +param+ is considered an uploaded file
|
199
|
+
# A parameter is considered to be an uploaded file if it is
|
200
|
+
# a hash and contains all parameters that Rack assigns to an
|
201
|
+
# uploaded file
|
202
|
+
#
|
203
|
+
# @param [Hash] param A request parameter
|
204
|
+
# @return [Boolean]
|
205
|
+
def is_uploaded_file?(param)
|
206
|
+
if param.respond_to?(:has_key?)
|
207
|
+
[:filename, :type, :name, :tempfile, :head].each do |k|
|
208
|
+
return false if !param.has_key?(k)
|
209
|
+
end
|
210
|
+
|
211
|
+
return true
|
212
|
+
else
|
213
|
+
return false
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Helper class methods. Methods in this module will be available
|
218
|
+
# in your controller *class* (not your controller instance).
|
219
|
+
module ClassMethods
|
220
|
+
include Ramaze::Traited
|
221
|
+
|
222
|
+
# Default options for uploaded files. You can change these options
|
223
|
+
# by using the uploads_options method
|
224
|
+
trait :default_upload_options => {
|
225
|
+
:allow_overwrite => false,
|
226
|
+
:autosave => false,
|
227
|
+
:default_upload_dir => nil,
|
228
|
+
:unlink_tempfile => false
|
229
|
+
}.freeze
|
230
|
+
|
231
|
+
##
|
232
|
+
# This method will activate automatic handling of uploaded files
|
233
|
+
# for specified actions in the controller.
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# class MyController < Ramaze::Controller
|
237
|
+
#
|
238
|
+
# # Use upload helper
|
239
|
+
# helper :upload
|
240
|
+
#
|
241
|
+
# # Handle all uploads for the foo and bar actions
|
242
|
+
# handle_uploads_for :foo, :bar
|
243
|
+
#
|
244
|
+
# # Handle all uploads for the baz action and uploads beginning with
|
245
|
+
# # 'up' for the qux action
|
246
|
+
# handle_uploads_for :baz, [:qux, /^up.*/]
|
247
|
+
# end
|
248
|
+
#
|
249
|
+
# @param *args An arbitrary long list of arguments with action names
|
250
|
+
# (and optionally patterns) that should handle file uploads
|
251
|
+
# automatically. Each argument can either be a symbol or a two-element
|
252
|
+
# array consisting of a symbol and a reqexp.
|
253
|
+
# @see #handle_all_uploads
|
254
|
+
# @see Ramaze::Helper::Upload#get_uploaded_files
|
255
|
+
def handle_uploads_for(*args)
|
256
|
+
args.each do |arg|
|
257
|
+
if arg.respond_to?(:first) and arg.respond_to?(:last)
|
258
|
+
before(arg.first.to_sym) do
|
259
|
+
get_uploaded_files(arg.last)
|
260
|
+
end
|
261
|
+
else
|
262
|
+
before(arg.to_sym) do
|
263
|
+
get_uploaded_files
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
##
|
270
|
+
# Sets options for file uploads in the controller.
|
271
|
+
#
|
272
|
+
# @example
|
273
|
+
# # This controller will handle all file uploads automatically.
|
274
|
+
# # All uploaded files are saved automatically in '/uploads/myapp'
|
275
|
+
# # and old files are overwritten.
|
276
|
+
# #
|
277
|
+
# class MyController < Ramaze::Controller
|
278
|
+
#
|
279
|
+
# # Use upload helper
|
280
|
+
# helper :upload
|
281
|
+
#
|
282
|
+
# handle_all_uploads
|
283
|
+
# upload_options :allow_overwrite => true,
|
284
|
+
# :autosave => true,
|
285
|
+
# :default_upload_dir => '/uploads/myapp',
|
286
|
+
# :unlink_tempfile => true
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
# # This controller will handle all file uploads automatically.
|
290
|
+
# # All uploaded files are saved automatically, but the exact location
|
291
|
+
# # is depending on a session variable. Old files are overwritten.
|
292
|
+
# #
|
293
|
+
# class MyController2 < Ramaze::Controller
|
294
|
+
#
|
295
|
+
# # Use upload helper
|
296
|
+
# helper :upload
|
297
|
+
#
|
298
|
+
# # Proc to use for save directory calculation
|
299
|
+
# calculate_dir = lambda { File.join('/uploads', session['user']) }
|
300
|
+
#
|
301
|
+
# handle_all_uploads
|
302
|
+
# upload_options :allow_overwrite => true,
|
303
|
+
# :autosave => true,
|
304
|
+
# :default_upload_dir => calculate_dir,
|
305
|
+
# :unlink_tempfile => true
|
306
|
+
# end
|
307
|
+
# @param [Hash] options Options controlling how file uploads
|
308
|
+
# are handled.
|
309
|
+
# @option options [Boolean] :allow_overwrite If set to *true*, uploaded
|
310
|
+
# files are allowed to overwrite existing ones. This option is set to
|
311
|
+
# *false* by default.
|
312
|
+
# @option options [Boolean] :autosave If set to *true*,
|
313
|
+
# Ramaze::Helper::Upload::UploadedFile#save will be called on all
|
314
|
+
# matched file uploads
|
315
|
+
# automatically. You can use this option to automatically save files
|
316
|
+
# at a preset location, but please note that you will need to set the
|
317
|
+
# :default_upload_dir (and possibly :allow_overwrite) options as well
|
318
|
+
# in order for this to work correctly. This option is set to *false*
|
319
|
+
# by default.
|
320
|
+
# @option options [String|Proc] :default_upload_dir If set to a string
|
321
|
+
# (representing a path in the file system) this option will allow you
|
322
|
+
# to save uploaded files without specifying a path. If you intend to
|
323
|
+
# call Ramaze::Helper::Upload::UploadedFile#save with a path you don't
|
324
|
+
# need to set this option at all. If you need to delay the calculation
|
325
|
+
# of the directory, you can also set this option to a proc. The proc
|
326
|
+
# should accept zero arguments and return a string. This comes in handy
|
327
|
+
# when you want to use different directory paths for different users
|
328
|
+
# etc. This option is set to *nil* by default.
|
329
|
+
# @option options [Boolean] :unlink_tempfile If set to *true*, this
|
330
|
+
# option will automatically unlink the temporary file created by Rack
|
331
|
+
# immediately after Ramaze::Helper::Upload::UploadedFile#save is done
|
332
|
+
# saving the uploaded file. This is probably not needed in most cases,
|
333
|
+
# but if you don't want to expose your uploaded files in a shared
|
334
|
+
# tempdir longer than necessary this option might be for you. This
|
335
|
+
# option is set to *false* by default.
|
336
|
+
# @see Ramaze::Helper::Upload::UploadedFile#initialize
|
337
|
+
# @see Ramaze::Helper::Upload::UploadedFile#save
|
338
|
+
#
|
339
|
+
def upload_options(options)
|
340
|
+
trait(
|
341
|
+
:upload_options => Ramaze::Helper::Upload::ClassMethods.trait[
|
342
|
+
:default_upload_options
|
343
|
+
].merge(options)
|
344
|
+
)
|
345
|
+
end
|
346
|
+
end # ClassMethods
|
347
|
+
|
348
|
+
##
|
349
|
+
# This class represents an uploaded file.
|
350
|
+
#
|
351
|
+
# @author Lars Olsson
|
352
|
+
# @since 18-08-2011
|
353
|
+
#
|
354
|
+
class UploadedFile
|
355
|
+
include Ramaze::Traited
|
356
|
+
|
357
|
+
# Suggested file name
|
358
|
+
# @return [String]
|
359
|
+
attr_reader :filename
|
360
|
+
|
361
|
+
# MIME-type
|
362
|
+
# @return [String]
|
363
|
+
attr_reader :type
|
364
|
+
|
365
|
+
##
|
366
|
+
# Initializes a new Ramaze::Helper::Upload::UploadedFile object.
|
367
|
+
#
|
368
|
+
# @param [String] filename Suggested file name
|
369
|
+
# @param [String] type MIME-type
|
370
|
+
# @param [File] tempfile temporary file
|
371
|
+
# @param [Hash] options Options for uploaded files. Options supported
|
372
|
+
# match those available to
|
373
|
+
# Ramaze::Helper::Upload::ClassMethods#upload_options
|
374
|
+
# @return [Ramaze::Helper::Upload::UploadedFile] A new
|
375
|
+
# Ramaze::Helper::Upload::UploadedFile object
|
376
|
+
# @see #save
|
377
|
+
# @see Ramaze::Helper::Upload::ClassMethods#upload_options
|
378
|
+
def initialize(filename, type, tempfile, options)
|
379
|
+
@filename = File.basename(filename)
|
380
|
+
@type = type
|
381
|
+
@tempfile = tempfile
|
382
|
+
@realfile = nil
|
383
|
+
|
384
|
+
trait :options => options
|
385
|
+
end
|
386
|
+
|
387
|
+
##
|
388
|
+
# Changes the suggested filename of this
|
389
|
+
# Ramaze::Helper::Upload::UploadedFile. +name+ should be a string
|
390
|
+
# representing the filename (only the filename, not a complete path),
|
391
|
+
# but if you provide a complete path this method it will try to identify
|
392
|
+
# the filename and use that instead.
|
393
|
+
#
|
394
|
+
# @param [String] The new suggested filename.
|
395
|
+
#
|
396
|
+
def filename=(name)
|
397
|
+
@filename = File.basename(name)
|
398
|
+
end
|
399
|
+
|
400
|
+
##
|
401
|
+
# Returns the path of the Ramaze::Helper::Upload::UploadedFile object.
|
402
|
+
# The method will always return *nil* before *save* has been called
|
403
|
+
# on the Ramaze::Helper::Upload::UploadedFile object.
|
404
|
+
#
|
405
|
+
# @return [String|nil]
|
406
|
+
#
|
407
|
+
def path
|
408
|
+
return self.saved? ? @realfile.path : nil
|
409
|
+
end
|
410
|
+
|
411
|
+
##
|
412
|
+
# Saves the Ramaze::Helper::Upload::UploadedFile.
|
413
|
+
#
|
414
|
+
# If +path+ is not set, the method checks whether there exists default
|
415
|
+
# options for the path and tries to use that instead.
|
416
|
+
#
|
417
|
+
# If you need to override any options set in the controller (using
|
418
|
+
# upload_options) you can set the corresponding option in +options+ to
|
419
|
+
# override the behavior for this particular
|
420
|
+
# Ramaze::Helper::Upload::UploadedFile object.
|
421
|
+
#
|
422
|
+
# @param [String] path Path where the
|
423
|
+
# Ramaze::Helper::Upload::UploadedFile will be saved
|
424
|
+
# @param [Hash] options Options for uploaded files. Options supported
|
425
|
+
# match those available to
|
426
|
+
# Ramaze::Helper::Upload::ClassMethods#upload_options
|
427
|
+
# @raise [StandardError] Will be raised if the save operation fails.
|
428
|
+
# @see #initialize
|
429
|
+
# @see Ramaze::Helper::Upload::ClassMethods#upload_options
|
430
|
+
#
|
431
|
+
def save(path = nil, options = {})
|
432
|
+
# Merge options
|
433
|
+
opts = trait[:options].merge(options)
|
434
|
+
|
435
|
+
unless path
|
436
|
+
# No path was provided, use info stored elsewhere to try to build
|
437
|
+
# the path
|
438
|
+
unless opts[:default_upload_dir]
|
439
|
+
raise StandardError.new('Unable to save file, no dirname given')
|
440
|
+
end
|
441
|
+
|
442
|
+
unless @filename
|
443
|
+
raise StandardError.new('Unable to save file, no filename given')
|
444
|
+
end
|
445
|
+
|
446
|
+
# Check to see if a proc or a string was used for the
|
447
|
+
# default_upload_dir parameter. If it was a proc, call the proc and
|
448
|
+
# use the result as the directory part of the path. If a string was
|
449
|
+
# used, use the string directly as the directory part of the path.
|
450
|
+
dn = opts[:default_upload_dir]
|
451
|
+
|
452
|
+
if dn.respond_to?(:call)
|
453
|
+
dn = dn.call
|
454
|
+
end
|
455
|
+
|
456
|
+
path = File.join(dn, @filename)
|
457
|
+
end
|
458
|
+
|
459
|
+
path = File.expand_path(path)
|
460
|
+
|
461
|
+
# Abort if file altready exists and overwrites are not allowed
|
462
|
+
if File.exists?(path) and !opts[:allow_overwrite]
|
463
|
+
raise StandardError.new('Unable to overwrite existing file')
|
464
|
+
end
|
465
|
+
|
466
|
+
# Confirm that we can read source file
|
467
|
+
unless File.readable?(@tempfile.path)
|
468
|
+
raise StandardError.new('Unable to read temporary file')
|
469
|
+
end
|
470
|
+
|
471
|
+
# Confirm that we can write to the destination file
|
472
|
+
unless (File.exists?(path) and File.writable?(path)) \
|
473
|
+
or (File.exists?(File.dirname(path)) \
|
474
|
+
and File.writable?(File.dirname(path)))
|
475
|
+
raise StandardError.new(
|
476
|
+
"Unable to save file to #{path}. Path is not writable"
|
477
|
+
)
|
478
|
+
end
|
479
|
+
|
480
|
+
# If supported, use IO,copy_stream. If not, require fileutils
|
481
|
+
# and use the same method from there
|
482
|
+
if IO.respond_to?(:copy_stream)
|
483
|
+
IO.copy_stream(@tempfile, path)
|
484
|
+
else
|
485
|
+
require 'fileutils'
|
486
|
+
File.open(@tempfile.path, 'rb') do |src|
|
487
|
+
File.open(path, 'wb') do |dest|
|
488
|
+
FileUtils.copy_stream(src, dest)
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# Update the realfile property, indicating that the file has been
|
494
|
+
# saved
|
495
|
+
@realfile = File.new(path)
|
496
|
+
|
497
|
+
# If the unlink_tempfile option is set to true, delete the temporary
|
498
|
+
# file created by Rack
|
499
|
+
unlink_tempfile if opts[:unlink_tempfile]
|
500
|
+
end
|
501
|
+
|
502
|
+
##
|
503
|
+
# Returns whether the Ramaze::Helper::Upload::UploadedFile has been
|
504
|
+
# saved or not.
|
505
|
+
#
|
506
|
+
# @return [Boolean]
|
507
|
+
#
|
508
|
+
def saved?
|
509
|
+
return !@realfile.nil?
|
510
|
+
end
|
511
|
+
|
512
|
+
##
|
513
|
+
# Deletes the temporary file associated with this
|
514
|
+
# Ramaze::Helper::Upload::UploadedFile immediately.
|
515
|
+
#
|
516
|
+
def unlink_tempfile
|
517
|
+
File.unlink(@tempfile.path)
|
518
|
+
@tempfile = nil
|
519
|
+
end
|
520
|
+
end # UploadedFile
|
521
|
+
end # Upload
|
522
|
+
end # Helper
|
523
|
+
end # Ramaze
|