ramaze 2008.06 → 2008.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. data/README.markdown +6 -6
  2. data/Rakefile +33 -3
  3. data/bin/ramaze +18 -0
  4. data/doc/CHANGELOG +960 -0
  5. data/doc/LEGAL +5 -1
  6. data/doc/meta/announcement.txt +20 -36
  7. data/doc/tutorial/todolist.html +421 -313
  8. data/doc/tutorial/todolist.mkd +33 -16
  9. data/examples/app/blog/spec/blog.rb +3 -3
  10. data/examples/app/rapaste/controller/paste.rb +8 -1
  11. data/examples/app/rapaste/model/paste.rb +3 -0
  12. data/examples/app/rapaste/spec/rapaste.rb +3 -1
  13. data/examples/app/rapaste/start.rb +3 -2
  14. data/examples/app/sourceview/public/sourceview.js +2 -2
  15. data/examples/app/todolist/spec/todolist.rb +6 -6
  16. data/examples/app/todolist/template/index.xhtml +1 -1
  17. data/examples/app/whywiki/spec/whywiki.rb +2 -2
  18. data/examples/app/wikore/spec/wikore.rb +2 -2
  19. data/examples/app/wikore/src/model.rb +4 -3
  20. data/examples/app/wiktacular/spec/wiktacular.rb +7 -7
  21. data/examples/basic/simple.rb +2 -2
  22. data/examples/helpers/paginate.rb +71 -0
  23. data/examples/misc/simple_auth.rb +20 -8
  24. data/lib/proto/controller/init.rb +10 -0
  25. data/lib/proto/controller/main.rb +1 -3
  26. data/lib/proto/model/init.rb +4 -0
  27. data/lib/proto/public/dispatch.fcgi +1 -1
  28. data/lib/proto/spec/main.rb +2 -1
  29. data/lib/proto/start.rb +3 -3
  30. data/lib/proto/start.ru +1 -1
  31. data/lib/proto/view/error.xhtml +4 -4
  32. data/lib/ramaze.rb +8 -3
  33. data/lib/ramaze/action.rb +6 -6
  34. data/lib/ramaze/adapter.rb +1 -6
  35. data/lib/ramaze/adapter/base.rb +30 -27
  36. data/lib/ramaze/adapter/cgi.rb +1 -0
  37. data/lib/ramaze/cache.rb +1 -3
  38. data/lib/ramaze/cache/memcached.rb +3 -5
  39. data/lib/ramaze/contrib/auto_params.rb +2 -2
  40. data/lib/ramaze/contrib/auto_params/get_args.rb +2 -1
  41. data/lib/ramaze/contrib/gems.rb +17 -18
  42. data/lib/ramaze/contrib/gzip_filter.rb +22 -9
  43. data/lib/ramaze/contrib/maruku_uv.rb +59 -0
  44. data/lib/ramaze/contrib/profiling.rb +1 -1
  45. data/lib/ramaze/contrib/rest.rb +16 -13
  46. data/lib/ramaze/contrib/sequel/create_join.rb +25 -0
  47. data/lib/ramaze/contrib/sequel/form_field.rb +129 -0
  48. data/lib/ramaze/contrib/sequel/image.rb +198 -0
  49. data/lib/ramaze/contrib/sequel/relation.rb +82 -0
  50. data/lib/ramaze/controller.rb +33 -34
  51. data/lib/ramaze/controller/resolve.rb +29 -9
  52. data/lib/ramaze/current.rb +60 -20
  53. data/lib/ramaze/current/request.rb +8 -7
  54. data/lib/ramaze/current/response.rb +15 -3
  55. data/lib/ramaze/current/session/flash.rb +8 -0
  56. data/lib/ramaze/dispatcher.rb +17 -9
  57. data/lib/ramaze/dispatcher/action.rb +4 -5
  58. data/lib/ramaze/dispatcher/directory.rb +1 -1
  59. data/lib/ramaze/dispatcher/error.rb +4 -4
  60. data/lib/ramaze/dispatcher/file.rb +4 -4
  61. data/lib/ramaze/gestalt.rb +15 -20
  62. data/lib/ramaze/helper/cgi.rb +7 -15
  63. data/lib/ramaze/helper/formatting.rb +41 -1
  64. data/lib/ramaze/helper/httpdigest.rb +20 -7
  65. data/lib/ramaze/helper/link.rb +4 -6
  66. data/lib/ramaze/helper/paginate.rb +233 -0
  67. data/lib/ramaze/helper/redirect.rb +1 -1
  68. data/lib/ramaze/helper/rest.rb +1 -1
  69. data/lib/ramaze/helper/thread.rb +17 -0
  70. data/lib/ramaze/helper/ultraviolet.rb +44 -0
  71. data/lib/ramaze/helper/user.rb +4 -9
  72. data/lib/ramaze/log.rb +2 -2
  73. data/lib/ramaze/log/analogger.rb +21 -23
  74. data/lib/ramaze/log/growl.rb +21 -23
  75. data/lib/ramaze/log/hub.rb +1 -1
  76. data/lib/ramaze/log/informer.rb +97 -99
  77. data/lib/ramaze/log/knotify.rb +14 -16
  78. data/lib/ramaze/log/logger.rb +11 -13
  79. data/lib/ramaze/log/logging.rb +61 -63
  80. data/lib/ramaze/log/rotatinginformer.rb +168 -0
  81. data/lib/ramaze/log/syslog.rb +41 -31
  82. data/lib/ramaze/log/xosd.rb +70 -72
  83. data/lib/ramaze/option.rb +9 -6
  84. data/lib/ramaze/option/holder.rb +5 -27
  85. data/lib/ramaze/reloader.rb +186 -0
  86. data/lib/ramaze/setup.rb +1 -1
  87. data/lib/ramaze/snippets.rb +13 -0
  88. data/lib/ramaze/snippets/array/put_within.rb +31 -24
  89. data/lib/ramaze/snippets/binding/locals.rb +23 -11
  90. data/lib/ramaze/snippets/dictionary.rb +2 -2
  91. data/lib/ramaze/snippets/fiber.rb +63 -0
  92. data/lib/ramaze/snippets/kernel/constant.rb +36 -21
  93. data/lib/ramaze/snippets/kernel/pretty_inspect.rb +12 -6
  94. data/lib/ramaze/snippets/numeric/filesize_format.rb +24 -17
  95. data/lib/ramaze/snippets/numeric/time.rb +63 -0
  96. data/lib/ramaze/snippets/object/__dir__.rb +29 -0
  97. data/lib/ramaze/snippets/object/acquire.rb +40 -0
  98. data/lib/ramaze/snippets/object/instance_variable_defined.rb +16 -5
  99. data/lib/ramaze/snippets/object/pretty.rb +14 -4
  100. data/lib/ramaze/snippets/object/scope.rb +14 -7
  101. data/lib/ramaze/snippets/ordered_set.rb +25 -14
  102. data/lib/ramaze/snippets/proc/locals.rb +17 -9
  103. data/lib/ramaze/snippets/ramaze/deprecated.rb +13 -0
  104. data/lib/ramaze/snippets/ramaze/fiber.rb +24 -0
  105. data/lib/ramaze/snippets/ramaze/state.rb +86 -0
  106. data/lib/ramaze/snippets/ramaze/struct.rb +45 -0
  107. data/lib/ramaze/snippets/string/camel_case.rb +13 -8
  108. data/lib/ramaze/snippets/string/color.rb +24 -20
  109. data/lib/ramaze/snippets/string/each.rb +14 -3
  110. data/lib/ramaze/snippets/string/end_with.rb +20 -0
  111. data/lib/ramaze/snippets/string/esc.rb +26 -21
  112. data/lib/ramaze/snippets/string/ord.rb +12 -6
  113. data/lib/ramaze/snippets/string/snake_case.rb +13 -7
  114. data/lib/ramaze/snippets/string/start_with.rb +16 -5
  115. data/lib/ramaze/snippets/string/unindent.rb +23 -15
  116. data/lib/ramaze/snippets/thread/into.rb +3 -3
  117. data/lib/ramaze/spec/helper/bacon.rb +5 -5
  118. data/lib/ramaze/spec/helper/mock_http.rb +1 -1
  119. data/lib/ramaze/spec/helper/pretty_output.rb +2 -2
  120. data/lib/ramaze/spec/helper/snippets.rb +8 -0
  121. data/lib/ramaze/template.rb +4 -1
  122. data/lib/ramaze/template/ezamar/textpow.syntax +34 -0
  123. data/lib/ramaze/template/maruku.rb +34 -0
  124. data/lib/ramaze/template/tagz.rb +2 -2
  125. data/lib/ramaze/template/xslt.rb +2 -2
  126. data/lib/ramaze/tool/create.rb +27 -53
  127. data/lib/ramaze/tool/localize.rb +8 -4
  128. data/lib/ramaze/tool/mime.rb +11 -1
  129. data/lib/ramaze/tool/project_creator.rb +110 -0
  130. data/lib/ramaze/trinity.rb +4 -1
  131. data/lib/ramaze/version.rb +1 -1
  132. data/lib/vendor/bacon.rb +323 -0
  133. data/rake_tasks/gem.rake +10 -1
  134. data/rake_tasks/maintenance.rake +40 -2
  135. data/rake_tasks/metric.rake +24 -0
  136. data/rake_tasks/release.rake +17 -4
  137. data/rake_tasks/spec.rake +1 -2
  138. data/ramaze.gemspec +549 -495
  139. data/spec/contrib/auto_params.rb +3 -3
  140. data/spec/contrib/profiling.rb +2 -2
  141. data/spec/examples/simple_auth.rb +2 -2
  142. data/spec/examples/templates/template_haml.rb +0 -2
  143. data/spec/ramaze/action/file_cache.rb +22 -0
  144. data/spec/ramaze/adapter.rb +2 -2
  145. data/spec/ramaze/controller/actionless_templates.rb +1 -1
  146. data/spec/ramaze/controller/subclass.rb +15 -0
  147. data/spec/ramaze/controller/template_resolving.rb +1 -1
  148. data/spec/ramaze/controller/view/bar.xhtml +1 -0
  149. data/spec/ramaze/controller/view/base/another.xhtml +1 -0
  150. data/spec/ramaze/current/session.rb +1 -1
  151. data/spec/ramaze/dispatcher/file.rb +2 -2
  152. data/spec/ramaze/helper/aspect.rb +26 -17
  153. data/spec/ramaze/helper/formatting.rb +13 -0
  154. data/spec/ramaze/log/informer.rb +10 -10
  155. data/spec/ramaze/log/syslog.rb +67 -4
  156. data/spec/ramaze/rewrite.rb +1 -1
  157. data/spec/ramaze/route.rb +1 -1
  158. data/spec/ramaze/struct.rb +47 -0
  159. data/spec/ramaze/template/markaby.rb +1 -1
  160. data/spec/ramaze/template/tagz.rb +1 -1
  161. data/spec/snippets/binding/locals.rb +9 -0
  162. data/spec/snippets/numeric/time.rb +12 -0
  163. data/spec/snippets/{kernel → object}/__dir__.rb +0 -0
  164. data/spec/snippets/{kernel → object}/acquire.rb +0 -0
  165. metadata +90 -81
  166. data/cache.yaml +0 -7
  167. data/examples/app/rammit/spec/rammit.rb +0 -31
  168. data/examples/app/rammit/src/controller/main.rb +0 -3
  169. data/examples/app/rammit/src/controller/page.rb +0 -16
  170. data/examples/app/rammit/src/model.rb +0 -33
  171. data/examples/app/rammit/start.rb +0 -8
  172. data/examples/app/rammit/template/index.xhtml +0 -14
  173. data/examples/app/rammit/template/page/view.xhtml +0 -4
  174. data/lib/ramaze/snippets/kernel/__dir__.rb +0 -23
  175. data/lib/ramaze/snippets/kernel/acquire.rb +0 -34
  176. data/lib/ramaze/snippets/object/thread_accessor.rb +0 -5
  177. data/lib/ramaze/snippets/ramaze/thread_accessor.rb +0 -58
  178. data/lib/ramaze/snippets/struct/fill.rb +0 -23
  179. data/lib/ramaze/snippets/struct/values_at.rb +0 -39
  180. data/lib/ramaze/snippets/symbol/to_proc.rb +0 -24
  181. data/lib/ramaze/sourcereload.rb +0 -176
  182. data/spec/snippets/struct/fill.rb +0 -26
  183. data/spec/snippets/struct/values_at.rb +0 -52
  184. data/spec/snippets/symbol/to_proc.rb +0 -13
@@ -43,13 +43,11 @@ module Ramaze
43
43
  nil
44
44
  end
45
45
 
46
- # out of some reason MemCache sometimes doesn't respond to
47
- # get_multi, have to investigate this further.
48
- #
49
- # for now, i'll just use the dumbed down version and ask it
50
- # whether it implements this functionality or not.
46
+ # Fetch for multiple keys at once.
51
47
 
52
48
  def get_multi(*keys)
49
+ # MemCache only responds to get_multi if the memcache-client_extensions gem
50
+ # is installed, so we check first and resort to another approach if not.
53
51
  if @cache.respond_to?(:get_multi)
54
52
  @cache.get_multi(*keys)
55
53
  else
@@ -47,11 +47,11 @@ module Ramaze
47
47
  self[:params] = par.map{ |pa|
48
48
  case pa
49
49
  when Array
50
- pa.map{|p| CGI.unescape(p.to_s)}
50
+ pa.map{|p| Rack::Utils.unescape(p)}
51
51
  when nil
52
52
  nil
53
53
  else
54
- CGI.unescape(pa.to_s)
54
+ Rack::Utils.unescape(pa)
55
55
  end
56
56
  } unless par.nil?
57
57
  self[:params] ||= []
@@ -1,5 +1,6 @@
1
1
  # from merb/core_ext/get_args.rb
2
2
  begin
3
+ require 'parse_tree'
3
4
  require 'ruby2ruby'
4
5
 
5
6
  class ParseTreeArray < Array
@@ -54,4 +55,4 @@ begin
54
55
  include GetArgs
55
56
  end
56
57
  rescue LoadError
57
- end
58
+ end
@@ -14,8 +14,12 @@ module Ramaze
14
14
  @gems << GemStone.new(name, options)
15
15
  end
16
16
 
17
+ # options include:
18
+ # :install_dir => where to install gems
19
+ # :extconf => additional options for building extensions
20
+
17
21
  def options opts = {}
18
- @options ||= { :docs => true }
22
+ @options ||= {}
19
23
  @options.merge! opts unless opts.empty?
20
24
  @options
21
25
  end
@@ -31,36 +35,31 @@ module Ramaze
31
35
 
32
36
  def initialize(name, options = {})
33
37
  @name, @options = name, options
38
+ require 'rubygems/dependency_installer'
39
+ @installer = Gem::DependencyInstaller.new(@options)
34
40
  end
35
41
 
36
42
  def setup(ran = false)
37
- # rubygems resets the path after each successful install
38
- Gem.use_paths Gems.options[:install_dir] if Gems.options[:install_dir]
39
-
40
- Gem.activate(name, *[@options[:version]].compact)
43
+ Gem.activate(name, *[options[:version]].compact)
41
44
  require options[:lib] || name
42
45
  rescue LoadError => error
43
46
  puts error
44
47
  return if ran
45
- puts "attempting install"
46
48
  install
47
49
  setup(ran = true)
48
50
  end
49
51
 
50
52
  def install
51
- require 'rubygems/gem_runner'
52
- version, source = options.values_at(:version, :source)
53
-
54
- cmd = %w[install] << name
55
- cmd << "--no-rdoc" << "--no-ri" unless Gems.options[:docs]
56
- cmd << "--install-dir" << Gems.options[:install_dir] if Gems.options[:install_dir]
57
- cmd << "--version" << version if version
58
- cmd << "--source" << source if source
53
+ if extconf = (options[:extconf] || Gems.options[:extconf])
54
+ old_argv = ARGV.clone
55
+ ARGV.replace extconf.split(' ')
56
+ end
59
57
 
60
- puts cmd * ' '
61
- Gem::GemRunner.new.run(cmd)
62
- rescue Gem::SystemExitException => e
63
- raise unless e.exit_code == 0
58
+ print "Installing #{name}..."
59
+ @installer.install name, options[:version]
60
+ puts "done.\n\n"
61
+ ensure
62
+ ARGV.replace old_argv if extconf
64
63
  end
65
64
  end
66
65
  end
@@ -3,13 +3,22 @@
3
3
 
4
4
  # gzip_filter.rb
5
5
  #
6
- # Use this to compress "large" pages with gzip. All major browsers support gzipped pages.
7
- # This filter brought to you by your friends in #ramaze: Pistos, manveru, rikur and Kashia.
6
+ # Use this to compress "large" pages with gzip. All major browsers support
7
+ # gzipped pages.
8
+ # This filter brought to you by your friends in #ramaze:
9
+ # Pistos, manveru, rikur and Kashia.
8
10
  #
9
11
  # Usage, in start.rb:
10
12
  #
11
13
  # require 'ramaze/contrib/gzip_filter'
12
14
  # Ramaze::Dispatcher::Action::FILTER << Ramaze::Filter::Gzip
15
+ #
16
+ # Setting options (at any point in your code):
17
+ #
18
+ # Ramaze::Filter::Gzip.trait(
19
+ # :threshold => 1024,
20
+ # :content_type => /^text\/.+/
21
+ # )
13
22
 
14
23
  require 'zlib'
15
24
 
@@ -17,8 +26,9 @@ module Ramaze
17
26
  module Filter
18
27
  class Gzip
19
28
 
20
- trait :enabled => true
21
- trait :threshold => 32768 # bytes
29
+ trait :enabled => true,
30
+ :content_type => /^text\/.+/,
31
+ :threshold => 32768 # bytes
22
32
 
23
33
  class << self
24
34
 
@@ -27,21 +37,24 @@ module Ramaze
27
37
  # Enables being plugged into Dispatcher::Action::FILTER
28
38
 
29
39
  def call( response, options = {} )
30
- return response if not trait[ :enabled ]
31
- return response if response.body.nil?
32
- return response if response.body.respond_to?( :read )
40
+ return response unless trait[ :enabled ]
41
+ return response unless body = response.body
42
+ return response if body.respond_to?( :read )
33
43
 
34
44
  accepts = request.env[ 'HTTP_ACCEPT_ENCODING' ]
35
45
  return response if accepts.nil? || ( accepts !~ /(x-gzip|gzip)/ )
36
46
 
37
- if response.content_type == 'text/html' && response.body.size > trait[ :threshold ]
47
+ acceptable_size = body.size >= trait[ :threshold ]
48
+ acceptable_type = response.content_type =~ trait[:content_type]
49
+
50
+ if acceptable_type and acceptable_size
38
51
  output = StringIO.new
39
52
  def output.close
40
53
  # Zlib closes the file handle, so we want to circumvent this
41
54
  rewind
42
55
  end
43
56
  gz = Zlib::GzipWriter.new( output )
44
- gz.write( response.body )
57
+ gz.write( body )
45
58
  gz.close
46
59
 
47
60
  response.body = output.string
@@ -0,0 +1,59 @@
1
+ # A hack to make MaRuKu use UltraViolet for highlighting
2
+ # To specify the style to use use for example:
3
+ # uv_style: amy
4
+ # html_use_syntax: true
5
+ # code_lang: ruby
6
+ # In the document header area.
7
+
8
+ require 'maruku'
9
+
10
+ module MaRuKu
11
+ module Out
12
+ module HTML
13
+ def uv_highlight(source, lang, style)
14
+ require 'uv'
15
+
16
+ html = Uv.parse(source, 'xhtml', lang, lines = false, style)
17
+
18
+ # Prepare <code> containing <pre>
19
+ code = Document.new(html, :respect_whitespace => :all).root
20
+ code.name = 'code'
21
+ code.attributes['class'] = lang
22
+ code.attributes['lang'] = lang
23
+
24
+ # Prepare <pre>
25
+ pre = Element.new('pre')
26
+ pre << code
27
+ pre.attributes['class'] = style
28
+ pre
29
+ rescue => ex
30
+ puts ex
31
+ to_html_code_using_pre(source)
32
+ end
33
+
34
+ def to_html_code
35
+ source = self.raw_code
36
+ use_syntax = get_setting(:html_use_syntax)
37
+ uv_style = get_setting(:uv_style)
38
+
39
+ lang = self.attributes[:lang] || @doc.attributes[:code_lang]
40
+ lang ||= 'ruby' if attributes[:ruby]
41
+
42
+ # we always use syntax highlighting, this is a doc wiki
43
+ if lang and use_syntax
44
+ element = uv_highlight(source, lang, uv_style)
45
+ else
46
+ element = to_html_code_using_pre(source)
47
+ end
48
+
49
+ color = get_setting(:code_background_color)
50
+
51
+ if color != Globals[:code_background_color]
52
+ element.attributes['style'] = "background-color: #{color};"
53
+ end
54
+
55
+ add_ws element
56
+ end
57
+ end # HTML
58
+ end # Out
59
+ end # MaRuKu
@@ -1,7 +1,7 @@
1
1
  require "ruby-prof"
2
2
 
3
3
  module Ramaze
4
- module Dispatcher
4
+ class Dispatcher
5
5
  class ActionProfiler < Action
6
6
  def self.call(path)
7
7
  if RubyProf.running?
@@ -1,14 +1,17 @@
1
- Ramaze::Route['REST dispatch'] = lambda{|path, request|
2
- case request.request_method
3
- when 'GET'
4
- path << 'show/'
5
- when 'POST'
6
- path << 'create/'
7
- when 'PUT'
8
- path << 'update/'
9
- when 'DELETE'
10
- path << 'destroy/'
11
- else
12
- path
1
+ Ramaze::Rewrite['REST dispatch'] = lambda do |path, request|
2
+ path << '/' unless path[-1] == '/'
3
+
4
+ method = if request.request_method == 'POST' and request.params.has_key?('method')
5
+ request.params['method'].upcase
6
+ else
7
+ request.request_method
8
+ end
9
+
10
+ case method
11
+ when 'GET' then path << 'show/'
12
+ when 'POST' then path << 'create/'
13
+ when 'PUT' then path << 'update/'
14
+ when 'DELETE' then path << 'destroy/'
15
+ else path
13
16
  end
14
- }
17
+ end
@@ -0,0 +1,25 @@
1
+ require 'sequel'
2
+
3
+ module Sequel
4
+ class Model
5
+ # Force join table generation
6
+ #
7
+ # Usage:
8
+ # User.create_join(Article, 'articles_users')
9
+ # # or let the method figure out the correct join table using sequel
10
+ # # conventions.
11
+ # User.create_join(Article)
12
+ def self.create_join(to, name = nil)
13
+ from = self
14
+ name ||= [table_name.to_s, to.table_name.to_s].sort.join('_')
15
+ from_key = "#{from.table_name.to_s.singularize}_id"
16
+ to_key = "#{to.table_name.to_s.singularize}_id"
17
+
18
+ db.create_table! name do
19
+ primary_key :id
20
+ foreign_key from_key, :class => from
21
+ foreign_key to_key, :class => to
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,129 @@
1
+ require 'ramaze/gestalt'
2
+
3
+ # job = Job[4]
4
+ # puts CGI::pretty(FormField.new(job, :contract, show_errors = true, Job::CONTRACTS).to_s)
5
+
6
+ class FormField
7
+ attr_accessor :this, :field, :show_errors, :hint, :db_type, :id, :value
8
+
9
+ # show_errors may be a number indicating how many errors it should display at
10
+ # maximum, 0 displays none, -1 all
11
+
12
+ def initialize(this, field, show_errors = true, hint = nil)
13
+ @this, @field, @show_errors, @hint =
14
+ this, field.to_sym, show_errors, hint
15
+ @db_type = db_type
16
+ @id = "form_#{@field}"
17
+ @value = this.send(@field)
18
+ end
19
+
20
+ def db_type
21
+ if field_schema = this.class.db_schema[field]
22
+ field_schema[:db_type]
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def to_s
29
+ unless label = @this.class::FORM_LABEL[field]
30
+ raise("No FORM_LABEL for %p on %p" % [field, @this.class])
31
+ end
32
+
33
+ label += ':'
34
+ id = @id
35
+ _self = self
36
+
37
+ Ramaze::Gestalt.build{
38
+ div(:class => :pair){
39
+ label(:for => id){ label }
40
+ _self.build_tag(self)
41
+ _self.build_errors(self)
42
+ }
43
+ }
44
+ end
45
+
46
+ def password(gestalt)
47
+ gestalt.input(@args.merge :type => :password, :value => value)
48
+ end
49
+
50
+ def checkbox(gestalt)
51
+ @args[:checked] = :checked if value
52
+ gestalt.input(@args.merge :type => :checkbox)
53
+ end
54
+
55
+ def select(gestalt)
56
+ gestalt.select @args do
57
+ v = value.to_s
58
+ hint.each do |h|
59
+ h = h.to_s
60
+ if h == v
61
+ gestalt.option(:value => h, :selected => :selected){ h }
62
+ else
63
+ gestalt.option(:value => h){ h }
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def input(gestalt)
70
+ gestalt.input(@args.merge :type => :text, :value => value)
71
+ end
72
+
73
+ def file(gestalt)
74
+ gestalt.input(@args.merge :type => :file)
75
+ end
76
+
77
+ def textarea(gestalt)
78
+ gestalt.textarea(@args){ value.to_s }
79
+ end
80
+
81
+ def build_tag(gestalt)
82
+ @args = {:id => id, :name => field}
83
+
84
+ case hint
85
+ when :password
86
+ password(gestalt)
87
+ when :boolean, :checkbox
88
+ checkbox(gestalt)
89
+ when :textarea
90
+ textarea(gestalt)
91
+ when :file
92
+ file(gestalt)
93
+ when Array, Range
94
+ select(gestalt)
95
+ else
96
+ case db_type
97
+ when 'varchar', 'integer'
98
+ input(gestalt)
99
+ when 'boolean'
100
+ checkbox(gestalt)
101
+ when 'string'
102
+ textarea(gestalt)
103
+ else
104
+ raise "Unsupported type: (#{db_type || hint} : #{field})"
105
+ end
106
+ end
107
+ end
108
+
109
+ def build_errors(gestalt)
110
+ each_error do |error|
111
+ gestalt.span(:class => :error){ error }
112
+ end
113
+ end
114
+
115
+ # TODO: suggest 'validated?' feature to sequel
116
+ def each_error(&block)
117
+ return unless show_errors
118
+
119
+ this.validate if this.errors.empty?
120
+ this.errors[field].first(show_errors).each(&block)
121
+ end
122
+
123
+ module Model
124
+ def form_field(field, hint = nil)
125
+ show_errors = Ramaze::Request.current.post? ? 1 : 0
126
+ FormField.new(self, field, show_errors, hint).to_s
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,198 @@
1
+ # Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ # Scaffold image models utilizing thumbnailing and Ramaze integration.
5
+ # Resizing is done by ImageScience.
6
+ #
7
+ # Usage:
8
+ # class Avatar < Sequel::Model
9
+ # IMAGE = {
10
+ # # specifies belongs_to, will create relation and foreign key
11
+ #
12
+ # :owner => :User,
13
+ #
14
+ #
15
+ # # Remove original and thumbnails on Avatar#destroy
16
+ #
17
+ # :cleanup => true,
18
+ #
19
+ #
20
+ # # Algorithm to use in ImageScience
21
+ # #
22
+ # # * resize(width, height)
23
+ # # Resizes the image to +width+ and +height+ using a cubic-bspline
24
+ # # filter.
25
+ # #
26
+ # # * thumbnail(size)
27
+ # # Creates a proportional thumbnail of the image scaled so its
28
+ # # longest edge is resized to +size+.
29
+ # #
30
+ # # * cropped_thumbnail(size)
31
+ # # Creates a square thumbnail of the image cropping the longest edge
32
+ # # to match the shortest edge, resizes to +size+.
33
+ #
34
+ # :agorithm => :thumbnail,
35
+ #
36
+ #
37
+ # # Key specifies the filename and accessors, value are arguments to the
38
+ # # algorithm
39
+ #
40
+ # :sizes => {
41
+ # :small => 150,
42
+ # :medium => 300,
43
+ # :large => 600
44
+ # }
45
+ # }
46
+ #
47
+ # # Perform the scaffold
48
+ # include SequelImage
49
+ # end
50
+
51
+ module SequelImage
52
+ def self.included(model)
53
+ args = model::IMAGE
54
+ set_foreign_key = args[:foreign_key] || "#{args[:owner]}_id".downcase.to_sym
55
+ set_belongs_to = args[:belongs_to] || args[:owner].to_s.downcase.to_sym
56
+
57
+ # Define schema
58
+ model.set_schema do
59
+ primary_key :id
60
+
61
+ varchar :original # path to the original image
62
+ varchar :mime, :size => 22 # average of /etc/mime.types
63
+
64
+ time :created_at
65
+ time :updated_at
66
+
67
+ foreign_key set_foreign_key
68
+ end
69
+
70
+ # Define Relations
71
+ model.belongs_to set_belongs_to
72
+
73
+ # Define Hooks
74
+ model.send(:hooks).clear
75
+
76
+ model.before_create do
77
+ generate_thumbnails
78
+ self.created_at = Time.now
79
+ end
80
+
81
+ model.before_save do
82
+ self.updated_at = Time.now
83
+ end
84
+
85
+ model.before_destroy do
86
+ cleanup if conf[:cleanup]
87
+ end
88
+
89
+ # Define singleton methods
90
+ model.extend(SingletonMethods)
91
+
92
+ # Define instance methods
93
+ model.send(:include,
94
+ InstanceMethods,
95
+ Ramaze::Helper::CGI,
96
+ Ramaze::Helper::Link)
97
+
98
+ args[:sizes].each do |size, *args|
99
+ model.send(:define_method, size){ public_file(size) }
100
+ model.send(:define_method, "#{size}_url"){ file(size) }
101
+ end
102
+ end
103
+
104
+ module SingletonMethods
105
+ def store(file, uid, hash = {})
106
+ image = new(hash)
107
+
108
+ type = file[:type]
109
+ filename = file[:filename]
110
+ tempfile = file[:tempfile]
111
+ raise ArgumentError, 'Empty tempfile' if tempfile.size == 0
112
+
113
+ ext = Ramaze::Tool::MIME.ext_for(type)
114
+ image.mime = type
115
+ target_name = image.next_name(File.basename(filename, File.extname(filename)), ext)
116
+ target_path = File.join(image.public_root, image.path, target_name)
117
+
118
+ FileUtils.mkdir_p(File.dirname(target_path))
119
+ FileUtils.cp(tempfile.path, target_path)
120
+
121
+ image.original = target_path
122
+ image.save
123
+ end
124
+ end
125
+
126
+ module InstanceMethods
127
+ def file(size = nil)
128
+ File.join('/', path, filename(size))
129
+ end
130
+
131
+ def public_file(size)
132
+ File.join(public_path, filename(size))
133
+ end
134
+
135
+ def public_path
136
+ File.join(public_root, path)
137
+ end
138
+
139
+ def path
140
+ conf[:path] || conf[:owner].to_s.downcase
141
+ end
142
+
143
+ def next_name(uid, ext)
144
+ uid = uid.to_s.scan(%r![^\\/'".:?&;\s]+!).join('-')
145
+ "#{uid}#{ext}"
146
+ end
147
+
148
+ def basename
149
+ File.basename(original, File.extname(original))
150
+ end
151
+
152
+ def public_root
153
+ Ramaze::Global.public_root
154
+ end
155
+
156
+ def filename(size)
157
+ if size
158
+ "#{basename}_#{size}.png"
159
+ else
160
+ "#{basename}.png"
161
+ end
162
+ end
163
+
164
+ def conf
165
+ self.class::IMAGE
166
+ end
167
+
168
+ def cleanup
169
+ conf[:sizes].each do |name, args|
170
+ out = public_file(name)
171
+ Ramaze::Log.debug "Remove Thumbnail: #{out}"
172
+ FileUtils.rm_f(out)
173
+ end
174
+
175
+ Ramaze::Log.debug "Remove original: #{original}"
176
+ FileUtils.rm_f(original)
177
+ end
178
+
179
+ def generate_thumbnails
180
+ FileUtils.mkdir_p(public_path)
181
+
182
+ sizes, algorithm = conf.values_at(:sizes, :algorithm)
183
+
184
+ ImageScience.with_image(original) do |img|
185
+ Ramaze::Log.debug "Process original: #{original}"
186
+
187
+ sizes.each do |name, args|
188
+ out = public_file(name)
189
+ Ramaze::Log.debug "Generate Thumbnail: #{out}"
190
+
191
+ img.send(algorithm, *args) do |thumb|
192
+ thumb.save(out)
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end