sinatra-contrib 1.3.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 (79) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +135 -0
  3. data/Rakefile +75 -0
  4. data/ideas.md +29 -0
  5. data/lib/sinatra/capture.rb +42 -0
  6. data/lib/sinatra/config_file.rb +151 -0
  7. data/lib/sinatra/content_for.rb +111 -0
  8. data/lib/sinatra/contrib.rb +39 -0
  9. data/lib/sinatra/contrib/all.rb +2 -0
  10. data/lib/sinatra/contrib/setup.rb +53 -0
  11. data/lib/sinatra/contrib/version.rb +45 -0
  12. data/lib/sinatra/cookies.rb +331 -0
  13. data/lib/sinatra/decompile.rb +113 -0
  14. data/lib/sinatra/engine_tracking.rb +96 -0
  15. data/lib/sinatra/extension.rb +95 -0
  16. data/lib/sinatra/json.rb +134 -0
  17. data/lib/sinatra/link_header.rb +132 -0
  18. data/lib/sinatra/multi_route.rb +81 -0
  19. data/lib/sinatra/namespace.rb +282 -0
  20. data/lib/sinatra/reloader.rb +384 -0
  21. data/lib/sinatra/respond_with.rb +245 -0
  22. data/lib/sinatra/streaming.rb +267 -0
  23. data/lib/sinatra/test_helpers.rb +87 -0
  24. data/sinatra-contrib.gemspec +125 -0
  25. data/spec/capture_spec.rb +80 -0
  26. data/spec/config_file/key_value.yml +6 -0
  27. data/spec/config_file/missing_env.yml +4 -0
  28. data/spec/config_file/with_envs.yml +7 -0
  29. data/spec/config_file/with_nested_envs.yml +11 -0
  30. data/spec/config_file_spec.rb +44 -0
  31. data/spec/content_for/different_key.erb +1 -0
  32. data/spec/content_for/different_key.erubis +1 -0
  33. data/spec/content_for/different_key.haml +2 -0
  34. data/spec/content_for/different_key.slim +2 -0
  35. data/spec/content_for/layout.erb +1 -0
  36. data/spec/content_for/layout.erubis +1 -0
  37. data/spec/content_for/layout.haml +1 -0
  38. data/spec/content_for/layout.slim +1 -0
  39. data/spec/content_for/multiple_blocks.erb +4 -0
  40. data/spec/content_for/multiple_blocks.erubis +4 -0
  41. data/spec/content_for/multiple_blocks.haml +8 -0
  42. data/spec/content_for/multiple_blocks.slim +8 -0
  43. data/spec/content_for/multiple_yields.erb +3 -0
  44. data/spec/content_for/multiple_yields.erubis +3 -0
  45. data/spec/content_for/multiple_yields.haml +3 -0
  46. data/spec/content_for/multiple_yields.slim +3 -0
  47. data/spec/content_for/passes_values.erb +1 -0
  48. data/spec/content_for/passes_values.erubis +1 -0
  49. data/spec/content_for/passes_values.haml +1 -0
  50. data/spec/content_for/passes_values.slim +1 -0
  51. data/spec/content_for/same_key.erb +1 -0
  52. data/spec/content_for/same_key.erubis +1 -0
  53. data/spec/content_for/same_key.haml +2 -0
  54. data/spec/content_for/same_key.slim +2 -0
  55. data/spec/content_for/takes_values.erb +1 -0
  56. data/spec/content_for/takes_values.erubis +1 -0
  57. data/spec/content_for/takes_values.haml +3 -0
  58. data/spec/content_for/takes_values.slim +3 -0
  59. data/spec/content_for_spec.rb +201 -0
  60. data/spec/cookies_spec.rb +782 -0
  61. data/spec/decompile_spec.rb +44 -0
  62. data/spec/extension_spec.rb +33 -0
  63. data/spec/json_spec.rb +115 -0
  64. data/spec/link_header_spec.rb +100 -0
  65. data/spec/multi_route_spec.rb +45 -0
  66. data/spec/namespace/foo.erb +1 -0
  67. data/spec/namespace/nested/foo.erb +1 -0
  68. data/spec/namespace_spec.rb +623 -0
  69. data/spec/okjson.rb +581 -0
  70. data/spec/reloader/app.rb.erb +40 -0
  71. data/spec/reloader_spec.rb +441 -0
  72. data/spec/respond_with/bar.erb +1 -0
  73. data/spec/respond_with/bar.json.erb +1 -0
  74. data/spec/respond_with/foo.html.erb +1 -0
  75. data/spec/respond_with/not_html.sass +2 -0
  76. data/spec/respond_with_spec.rb +289 -0
  77. data/spec/spec_helper.rb +6 -0
  78. data/spec/streaming_spec.rb +436 -0
  79. metadata +256 -0
@@ -0,0 +1,113 @@
1
+ require 'sinatra/base'
2
+ require 'backports'
3
+ require 'uri'
4
+
5
+ module Sinatra
6
+
7
+ # = Sinatra::Decompile
8
+ #
9
+ # <tt>Sinatra::Decompile</tt> is an extension that provides a method,
10
+ # conveniently called +decompile+, that will generate a String pattern for a
11
+ # given route.
12
+ #
13
+ # == Usage
14
+ #
15
+ # === Classic Application
16
+ #
17
+ # To use the extension in a classic application all you need to do is require
18
+ # it:
19
+ #
20
+ # require "sinatra"
21
+ # require "sinatra/decompile"
22
+ #
23
+ # # Your classic application code goes here...
24
+ #
25
+ # This will add the +decompile+ method to the application/class scope, but
26
+ # you can also call it as <tt>Sinatra::Decompile.decompile</tt>.
27
+ #
28
+ # === Modular Application
29
+ #
30
+ # To use the extension in a modular application you need to require it, and
31
+ # then, tell the application you will use it:
32
+ #
33
+ # require "sinatra/base"
34
+ # require "sinatra/decompile"
35
+ #
36
+ # class MyApp < Sinatra::Base
37
+ # register Sinatra::Decompile
38
+ #
39
+ # # The rest of your modular application code goes here...
40
+ # end
41
+ #
42
+ # This will add the +decompile+ method to the application/class scope. You
43
+ # can choose not to register the extension, but instead of calling
44
+ # +decompile+, you will need to call <tt>Sinatra::Decompile.decompile</tt>.
45
+ #
46
+ module Decompile
47
+ extend self
48
+
49
+ ##
50
+ # Regenerates a string pattern for a given route
51
+ #
52
+ # Example:
53
+ #
54
+ # class Sinatra::Application
55
+ # routes.each do |verb, list|
56
+ # puts "#{verb}:"
57
+ # list.each do |data|
58
+ # puts "\t" << decompile(data)
59
+ # end
60
+ # end
61
+ # end
62
+ #
63
+ # Will return the internal Regexp if unable to reconstruct the pattern,
64
+ # which likely indicates that a Regexp was used in the first place.
65
+ #
66
+ # You can also use this to check whether you could actually use a string
67
+ # pattern instead of your regexp:
68
+ #
69
+ # decompile /^/foo$/ # => '/foo'
70
+ def decompile(pattern, keys = nil, *)
71
+ # Everything in here is basically just the reverse of
72
+ # Sinatra::Base#compile
73
+ pattern, keys = pattern if pattern.respond_to? :to_ary
74
+ keys, str = keys.try(:dup), pattern.inspect
75
+ return pattern unless str.start_with? '/' and str.end_with? '/'
76
+ str.gsub! /^\/\^?|\$?\/$/, ''
77
+ str.gsub! encoded(' '), ' '
78
+ return pattern if str =~ /^[\.\+]/
79
+ str.gsub! /\([^\(\)]*\)/ do |part|
80
+ case part
81
+ when '(.*?)'
82
+ return pattern if keys.shift != 'splat'
83
+ '*'
84
+ when '([^\/?#]+)'
85
+ return pattern if keys.empty?
86
+ ":" << keys.shift
87
+ when /^\(\?\:\\?(.)\|/
88
+ char = $1
89
+ return pattern unless encoded(char) == part
90
+ Regexp.escape(char)
91
+ else
92
+ return pattern
93
+ end
94
+ end
95
+ str.gsub /(.)([\.\+\(\)\/])/ do
96
+ return pattern if $1 != "\\"
97
+ $2
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def encoded(char)
104
+ return super if defined? super
105
+ enc = URI.encode(char)
106
+ enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
107
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
108
+ enc
109
+ end
110
+ end
111
+
112
+ register Decompile
113
+ end
@@ -0,0 +1,96 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+ module EngineTracking
5
+ attr_reader :current_engine
6
+
7
+ def erb?
8
+ @current_engine == :erb
9
+ end
10
+
11
+ def erubis?
12
+ @current_engine == :erubis or
13
+ erb? && Tilt[:erb] == Tilt::ErubisTemplate
14
+ end
15
+
16
+ def haml?
17
+ @current_engine == :haml
18
+ end
19
+
20
+ def sass?
21
+ @current_engine == :sass
22
+ end
23
+
24
+ def scss?
25
+ @current_engine == :scss
26
+ end
27
+
28
+ def less?
29
+ @current_engine == :less
30
+ end
31
+
32
+ def builder?
33
+ @current_engine == :builder
34
+ end
35
+
36
+ def liquid?
37
+ @current_engine == :liquid
38
+ end
39
+
40
+ def markdown?
41
+ @current_engine == :markdown
42
+ end
43
+
44
+ def textile?
45
+ @current_engine == :textile
46
+ end
47
+
48
+ def rdoc?
49
+ @current_engine == :rdoc
50
+ end
51
+
52
+ def radius?
53
+ @current_engine == :radius
54
+ end
55
+
56
+ def markaby?
57
+ @current_engine == :markaby
58
+ end
59
+
60
+ def coffee?
61
+ @current_engine == :coffee
62
+ end
63
+
64
+ def nokogiri?
65
+ @current_engine == :nokogiri
66
+ end
67
+
68
+ def slim?
69
+ @current_engine == :slim
70
+ end
71
+
72
+ def creole?
73
+ @current_engine == :creole
74
+ end
75
+
76
+ def initialize(*)
77
+ @current_engine = :ruby
78
+ super
79
+ end
80
+
81
+ def with_engine(engine)
82
+ @current_engine, engine_was = engine.to_sym, @current_engine
83
+ yield
84
+ ensure
85
+ @current_engine = engine_was
86
+ end
87
+
88
+ private
89
+
90
+ def render(engine, *)
91
+ with_engine(engine) { super }
92
+ end
93
+ end
94
+
95
+ helpers EngineTracking
96
+ end
@@ -0,0 +1,95 @@
1
+ require 'sinatra/base'
2
+ require 'backports/basic_object' unless defined? BasicObject
3
+
4
+ module Sinatra
5
+
6
+ # = Sinatra::Extension
7
+ #
8
+ # <tt>Sinatra::Extension</tt> is a mixin that provides some syntactic sugar
9
+ # for your extensions. It allows you to call directly inside your extension
10
+ # module almost any <tt>Sinatra::Base</tt> method. This means you can use
11
+ # +get+ to define a route, +before+ to define a before filter, +set+ to
12
+ # define a setting, a so on.
13
+ #
14
+ # Is important to be aware that this mixin remembers the methods calls you
15
+ # make, and then, when your extension is registered, replays them on the
16
+ # Sinatra application that has been extended. In order to do that, it
17
+ # defines a <tt>registered</tt> method, so, if your extension defines one
18
+ # too, remember to call +super+.
19
+ #
20
+ # == Usage
21
+ #
22
+ # Just require the mixin and extend your extension with it:
23
+ #
24
+ # require 'sinatra/extension'
25
+ #
26
+ # module MyExtension
27
+ # extend Sinatra::Extension
28
+ #
29
+ # # set some settings for development
30
+ # configure :development do
31
+ # set :reload_stuff, true
32
+ # end
33
+ #
34
+ # # define a route
35
+ # get '/' do
36
+ # 'Hello World'
37
+ # end
38
+ #
39
+ # # The rest of your extension code goes here...
40
+ # end
41
+ #
42
+ # You can also create an extension with the +new+ method:
43
+ #
44
+ # MyExtension = Sinatra::Extension.new do
45
+ # # Your extension code goes here...
46
+ # end
47
+ #
48
+ # This is useful when you just want to pass a block to
49
+ # <tt>Sinatra::Base.register</tt>.
50
+ module Extension
51
+ def self.new(&block)
52
+ ext = Module.new.extend(self)
53
+ ext.class_eval(&block)
54
+ ext
55
+ end
56
+
57
+ def settings
58
+ self
59
+ end
60
+
61
+ def configure(*args, &block)
62
+ record(:configure, *args) { |c| c.instance_exec(c, &block) }
63
+ end
64
+
65
+ def registered(base = nil, &block)
66
+ base ? replay(base) : record(:class_eval, &block)
67
+ end
68
+
69
+ private
70
+
71
+ def record(method, *args, &block)
72
+ recorded_methods << [method, args, block]
73
+ end
74
+
75
+ def replay(object)
76
+ recorded_methods.each { |m, a, b| object.send(m, *a, &b) }
77
+ end
78
+
79
+ def recorded_methods
80
+ @recorded_methods ||= []
81
+ end
82
+
83
+ def method_missing(method, *args, &block)
84
+ return super unless Sinatra::Base.respond_to? method
85
+ record(method, *args, &block)
86
+ DontCall.new(method)
87
+ end
88
+
89
+ class DontCall < BasicObject
90
+ def initialize(method) @method = method end
91
+ def method_missing(*) fail "not supposed to use result of #@method!" end
92
+ def inspect; "#<#{self.class}: #{@method}>" end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,134 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+
5
+ # = Sinatra::JSON
6
+ #
7
+ # <tt>Sinatra::JSON</tt> adds a helper method, called +json+, for (obviously)
8
+ # json generation.
9
+ #
10
+ # == Usage
11
+ #
12
+ # === Classic Application
13
+ #
14
+ # In a classic application simply require the helper, and start using it:
15
+ #
16
+ # require "sinatra"
17
+ # require "sinatra/json"
18
+ #
19
+ # # define a route that uses the helper
20
+ # get '/' do
21
+ # json :foo => 'bar'
22
+ # end
23
+ #
24
+ # # The rest of your classic application code goes here...
25
+ #
26
+ # === Modular Application
27
+ #
28
+ # In a modular application you need to require the helper, and then tell the
29
+ # application you will use it:
30
+ #
31
+ # require "sinatra/base"
32
+ # require "sinatra/json"
33
+ #
34
+ # class MyApp < Sinatra::Base
35
+ # helpers Sinatra::JSON
36
+ #
37
+ # # define a route that uses the helper
38
+ # get '/' do
39
+ # json :foo => 'bar'
40
+ # end
41
+ #
42
+ # # The rest of your modular application code goes here...
43
+ # end
44
+ #
45
+ # === Encoders
46
+ #
47
+ # Per default it will try to call +to_json+ on the object, but if it doesn't
48
+ # respond to that message, will use its own, rather simple encoder. You can
49
+ # easily change that anyways. To use +JSON+, simply require it:
50
+ #
51
+ # require 'json'
52
+ #
53
+ # The same goes for <tt>Yajl::Encoder</tt>:
54
+ #
55
+ # require 'yajl'
56
+ #
57
+ # For other encoders, besides requiring them, you need to define the
58
+ # <tt>:json_encoder</tt> setting. For instance, for the +Whatever+ encoder:
59
+ #
60
+ # require 'whatever'
61
+ # set :json_encoder, Whatever
62
+ #
63
+ # To force +json+ to simply call +to_json+ on the object:
64
+ #
65
+ # set :json_encoder, :to_json
66
+ #
67
+ # Actually, it can call any method:
68
+ #
69
+ # set :json_encoder, :my_fancy_json_method
70
+ #
71
+ # === Content-Type
72
+ #
73
+ # It will automatically set the content type to "application/json". As
74
+ # usual, you can easily change that, with the <tt>:json_content_type</tt>
75
+ # setting:
76
+ #
77
+ # set :json_content_type, :js
78
+ #
79
+ # === Overriding the Encoder and the Content-Type
80
+ #
81
+ # The +json+ helper will also take two options <tt>:encoder</tt> and
82
+ # <tt>:content_type</tt>. The values of this options are the same as the
83
+ # <tt>:json_encoder</tt> and <tt>:json_content_type</tt> settings,
84
+ # respectively. You can also pass those to the json method:
85
+ #
86
+ # get '/' do
87
+ # json({:foo => 'bar'}, :encoder => :to_json, :content_type => :js)
88
+ # end
89
+ #
90
+ module JSON
91
+ class << self
92
+ def encode(object)
93
+ enc object, Array, Hash
94
+ end
95
+
96
+ private
97
+
98
+ def enc(o, *a)
99
+ o = o.to_s if o.is_a? Symbol
100
+ fail "invalid: #{o.inspect}" unless a.empty? or a.include? o.class
101
+ case o
102
+ when Float then o.nan? || o.infinite? ? 'null' : o.inspect
103
+ when TrueClass, FalseClass, NilClass, Numeric, String then o.inspect
104
+ when Array then map(o, "[%s]") { |e| enc(e) }
105
+ when Hash then map(o, "{%s}") { |k,v| enc(k, String) + ":" + enc(v) }
106
+ end
107
+ end
108
+
109
+ def map(o, wrapper, &block)
110
+ wrapper % o.map(&block).join(',')
111
+ end
112
+ end
113
+
114
+ def json(object, options = {})
115
+ encoder = options[:encoder] || settings.json_encoder
116
+ content_type options[:content_type] || settings.json_content_type
117
+ if encoder.respond_to? :encode then encoder.encode(object)
118
+ elsif encoder.respond_to? :generate then encoder.generate(object)
119
+ elsif encoder.is_a? Symbol then object.__send__(encoder)
120
+ else fail "#{encoder} does not respond to #generate nor #encode"
121
+ end
122
+ end
123
+ end
124
+
125
+ Base.set :json_encoder do
126
+ return Yajl::Encoder if defined? Yajl::Encoder
127
+ return JSON if defined? JSON
128
+ return :to_json if {}.respond_to? :to_json and [].respond_to? :to_json
129
+ Sinatra::JSON
130
+ end
131
+
132
+ Base.set :json_content_type, :json
133
+ helpers JSON
134
+ end
@@ -0,0 +1,132 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+
5
+ # = Sinatra::LinkHeader
6
+ #
7
+ # <tt>Sinatra::LinkHeader</tt> adds a set of helper methods to generate link
8
+ # HTML tags and their corresponding Link HTTP headers.
9
+ #
10
+ # == Usage
11
+ #
12
+ # Once you had set up the helpers in your application (see below), you will
13
+ # be able to call the following methods from inside your route handlers,
14
+ # filters and templates:
15
+ #
16
+ # +prefetch+::
17
+ # Sets the Link HTTP headers and returns HTML tags to prefetch the given
18
+ # resources.
19
+ #
20
+ # +stylesheet+::
21
+ # Sets the Link HTTP headers and returns HTML tags to use the given
22
+ # stylesheets.
23
+ #
24
+ # +link+::
25
+ # Sets the Link HTTP headers and returns the corresponding HTML tags
26
+ # for the given resources.
27
+ #
28
+ # +link_headers+::
29
+ # Returns the corresponding HTML tags for the current Link HTTP headers.
30
+ #
31
+ # === Classic Application
32
+ #
33
+ # In a classic application simply require the helpers, and start using them:
34
+ #
35
+ # require "sinatra"
36
+ # require "sinatra/link_header"
37
+ #
38
+ # # The rest of your classic application code goes here...
39
+ #
40
+ # === Modular Application
41
+ #
42
+ # In a modular application you need to require the helpers, and then tell
43
+ # the application you will use them:
44
+ #
45
+ # require "sinatra/base"
46
+ # require "sinatra/link_header"
47
+ #
48
+ # class MyApp < Sinatra::Base
49
+ # helpers Sinatra::LinkHeader
50
+ #
51
+ # # The rest of your modular application code goes here...
52
+ # end
53
+ #
54
+ module LinkHeader
55
+ ##
56
+ # Set Link HTTP header and returns HTML tags for telling the browser to
57
+ # prefetch given resources (only supported by Opera and Firefox at the
58
+ # moment).
59
+ def prefetch(*urls)
60
+ link(:prefetch, *urls)
61
+ end
62
+
63
+ ##
64
+ # Sets Link HTTP header and returns HTML tags for using stylesheets.
65
+ def stylesheet(*urls)
66
+ urls << {} unless urls.last.respond_to? :to_hash
67
+ urls.last[:type] ||= mime_type(:css)
68
+ link(:stylesheet, *urls)
69
+ end
70
+
71
+ ##
72
+ # Sets Link HTTP header and returns corresponding HTML tags.
73
+ #
74
+ # Example:
75
+ #
76
+ # # Sets header:
77
+ # # Link: </foo>; rel="next"
78
+ # # Returns String:
79
+ # # '<link href="/foo" rel="next" />'
80
+ # link '/foo', :rel => :next
81
+ #
82
+ # # Multiple URLs
83
+ # link :stylesheet, '/a.css', '/b.css'
84
+ def link(*urls)
85
+ opts = urls.last.respond_to?(:to_hash) ? urls.pop : {}
86
+ opts[:rel] = urls.shift unless urls.first.respond_to? :to_str
87
+ options = opts.map { |k, v| " #{k}=#{v.to_s.inspect}" }
88
+ html_pattern = "<link href=\"%s\"#{options.join} />"
89
+ http_pattern = ["<%s>", *options].join ";"
90
+ link = (response["Link"] ||= "")
91
+
92
+ urls.map do |url|
93
+ link << "\n" unless link.empty?
94
+ link << (http_pattern % url)
95
+ html_pattern % url
96
+ end.join "\n"
97
+ end
98
+
99
+ ##
100
+ # Takes the current value of th Link header(s) and generates HTML tags
101
+ # from it.
102
+ #
103
+ # Example:
104
+ #
105
+ # get '/' do
106
+ # # You can of course use fancy helpers like #link, #stylesheet
107
+ # # or #prefetch
108
+ # response["Link"] = '</foo>; rel="next"'
109
+ # haml :some_page
110
+ # end
111
+ #
112
+ # __END__
113
+ #
114
+ # @@ layout
115
+ # %head= link_headers
116
+ # %body= yield
117
+ def link_headers
118
+ yield if block_given?
119
+ return "" unless response.include? "Link"
120
+ response["Link"].lines.map do |line|
121
+ url, *opts = line.split(';').map(&:strip)
122
+ "<link href=\"#{url[1..-2]}\" #{opts.join " "} />"
123
+ end.join "\n"
124
+ end
125
+
126
+ def self.registered(base)
127
+ puts "WARNING: #{self} is a helpers module, not an extension."
128
+ end
129
+ end
130
+
131
+ helpers LinkHeader
132
+ end