vanilla 1.17 → 1.17.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 (56) hide show
  1. data/Rakefile +21 -7
  2. data/bin/vanilla +2 -2
  3. data/lib/vanilla.rb +10 -7
  4. data/lib/vanilla/app.rb +57 -97
  5. data/lib/vanilla/config.rb +46 -0
  6. data/lib/vanilla/console.rb +1 -1
  7. data/lib/vanilla/renderers/base.rb +12 -2
  8. data/lib/vanilla/request.rb +9 -34
  9. data/lib/vanilla/routing.rb +34 -0
  10. data/lib/vanilla/test_helper.rb +10 -7
  11. data/pristine_app/Gemfile.lock +35 -0
  12. data/pristine_app/application.rb +2 -4
  13. data/pristine_app/public/vanilla.css +299 -9
  14. data/pristine_app/soups/base/layout.snip +11 -7
  15. data/pristine_app/soups/base/start.snip +15 -14
  16. data/pristine_app/soups/system/current_snip.rb +2 -2
  17. data/pristine_app/soups/system/feed.rb +30 -0
  18. data/pristine_app/soups/system/index.rb +3 -3
  19. data/pristine_app/soups/system/link_to.rb +5 -1
  20. data/pristine_app/soups/system/link_to_current_snip.rb +2 -3
  21. data/pristine_app/soups/tutorial/tutorial-layout.snip +9 -2
  22. data/pristine_app/soups/tutorial/tutorial-links.snip +2 -1
  23. data/pristine_app/soups/tutorial/tutorial-removing.snip.markdown +8 -0
  24. data/pristine_app/soups/tutorial/tutorial-renderers.snip.markdown +10 -4
  25. data/pristine_app/soups/tutorial/tutorial.snip.markdown +0 -1
  26. data/pristine_app/soups/tutorial/vanilla-rb.snip +4 -6
  27. data/pristine_app/tmp/restart.txt +0 -0
  28. data/test/core/configuration_test.rb +89 -0
  29. data/test/{dynasnip_test.rb → core/dynasnip_test.rb} +0 -0
  30. data/test/{renderers → core/renderers}/base_renderer_test.rb +37 -0
  31. data/test/{renderers → core/renderers}/erb_renderer_test.rb +0 -0
  32. data/test/{renderers → core/renderers}/haml_renderer_test.rb +0 -0
  33. data/test/{renderers → core/renderers}/markdown_renderer_test.rb +0 -0
  34. data/test/{renderers → core/renderers}/raw_renderer_test.rb +0 -0
  35. data/test/{renderers → core/renderers}/ruby_renderer_test.rb +0 -0
  36. data/test/core/routing_test.rb +30 -0
  37. data/test/{snip_inclusion_test.rb → core/snip_inclusion_test.rb} +0 -0
  38. data/test/{snip_reference_parser_test.rb → core/snip_reference_parser_test.rb} +0 -0
  39. data/test/{test_helper.rb → core/test_helper.rb} +5 -4
  40. data/test/core/vanilla_app_test.rb +51 -0
  41. data/test/{vanilla_presenting_test.rb → core/vanilla_presenting_test.rb} +15 -1
  42. data/test/{vanilla_request_test.rb → core/vanilla_request_test.rb} +0 -0
  43. data/test/pristine_app/current_snip_test.rb +46 -0
  44. data/test/pristine_app/feed_test.rb +47 -0
  45. data/test/pristine_app/index_test.rb +34 -0
  46. data/test/pristine_app/link_to_current_snip_test.rb +11 -0
  47. data/test/pristine_app/link_to_test.rb +27 -0
  48. data/test/pristine_app/page_title_test.rb +15 -0
  49. data/test/pristine_app/raw_test.rb +24 -0
  50. data/test/pristine_app/test_helper.rb +25 -0
  51. metadata +83 -42
  52. data/lib/vanilla/routes.rb +0 -18
  53. data/test/dynasnips/link_to_current_snip_test.rb +0 -19
  54. data/test/dynasnips/link_to_test.rb +0 -27
  55. data/test/dynasnips/page_title_test.rb +0 -19
  56. data/test/vanilla_app_test.rb +0 -111
data/Rakefile CHANGED
@@ -7,12 +7,24 @@ require "vanilla"
7
7
 
8
8
  task :default => :test
9
9
 
10
- require "rake/testtask"
11
- Rake::TestTask.new do |t|
12
- t.libs << "test"
13
- t.ruby_opts << "-rubygems"
14
- t.test_files = FileList["test/**/*_test.rb"]
15
- t.verbose = true
10
+ task :test => ["test:core", "test:app"]
11
+
12
+ namespace :test do
13
+ require "rake/testtask"
14
+ Rake::TestTask.new(:core) do |t|
15
+ t.libs << "test/core"
16
+ t.ruby_opts << "-rubygems"
17
+ t.test_files = FileList["test/core/**/*_test.rb"]
18
+ t.verbose = true
19
+ end
20
+
21
+ require "rake/testtask"
22
+ Rake::TestTask.new(:app) do |t|
23
+ t.libs << "test/pristine_app"
24
+ t.ruby_opts << "-rubygems"
25
+ t.test_files = FileList["test/pristine_app/**/*_test.rb"]
26
+ t.verbose = true
27
+ end
16
28
  end
17
29
 
18
30
  if Object.const_defined?(:Gem)
@@ -42,7 +54,7 @@ if Object.const_defined?(:Gem)
42
54
 
43
55
  # All the other gems we need.
44
56
  s.add_dependency("rack", ">= 0.9.1")
45
- s.add_dependency("soup", ">= 1.0.6")
57
+ s.add_dependency("soup", ">= 1.0.8")
46
58
  s.add_dependency("ratom", ">= 0.3.5")
47
59
  s.add_dependency("RedCloth", ">= 4.1.1")
48
60
  s.add_dependency("BlueCloth", ">= 1.0.0")
@@ -52,6 +64,8 @@ if Object.const_defined?(:Gem)
52
64
 
53
65
  s.add_development_dependency("kintama", ">= 0.1.6") # add any other gems for testing/development
54
66
  s.add_development_dependency("mocha")
67
+ s.add_development_dependency("capybara")
68
+ s.add_development_dependency("launchy")
55
69
 
56
70
  # If you want to publish automatically to rubyforge, you'll may need
57
71
  # to tweak this, and the publishing task below too.
data/bin/vanilla CHANGED
@@ -15,10 +15,10 @@ end
15
15
  def upgrade
16
16
  require 'fileutils'
17
17
  soups_directory = File.join(Dir.pwd, "soups")
18
- if File.directory(soups_directory)
18
+ if File.directory?(soups_directory)
19
19
  confirm("Upgrade system snips?") do
20
20
  pristine_system = File.expand_path("../../pristine_app/soups/system", __FILE__)
21
- copy(pristine_system, system_directory)
21
+ copy(pristine_system, soups_directory)
22
22
  end
23
23
  confirm("Upgrade tutorial snips?") do
24
24
  pristine_system = File.expand_path("../../pristine_app/soups/tutorial", __FILE__)
data/lib/vanilla.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  module Vanilla
2
- VERSION = "1.17"
2
+ VERSION = "1.17.1"
3
3
 
4
4
  autoload :Renderers, "vanilla/renderers"
5
5
  autoload :App, "vanilla/app"
6
+ autoload :Config, "vanilla/config"
6
7
  autoload :Dynasnip, "vanilla/dynasnip"
7
8
  autoload :Request, "vanilla/request"
8
- autoload :Routes, "vanilla/routes"
9
+ autoload :Routing, "vanilla/routing"
9
10
  autoload :Static, "vanilla/static"
10
11
  autoload :SnipReferenceParser, "vanilla/snip_reference_parser"
11
12
  autoload :TestHelper, "vanilla/test_helper"
12
- end
13
13
 
14
- # Load all the base dynasnip classes
15
- Dir[File.join(File.dirname(__FILE__), 'vanilla', 'dynasnips', '*.rb')].each do |dynasnip|
16
- require dynasnip
17
- end
14
+ class << self
15
+ # The set of currently loaded Vanilla::App subclasses
16
+ def apps
17
+ @apps ||= []
18
+ end
19
+ end
20
+ end
data/lib/vanilla/app.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'soup'
2
- require 'ostruct'
3
2
 
4
3
  module Vanilla
4
+
5
+ # This is the main App class for Vanilla applications; this should
6
+ # be subclassed for each instance of Vanilla that you want to run.
5
7
  class App
6
- include Vanilla::Routes
8
+ include Vanilla::Routing
7
9
 
8
10
  class << self
9
11
  attr_reader :config
@@ -13,48 +15,23 @@ module Vanilla
13
15
  self
14
16
  end
15
17
  def reset!
16
- @config = OpenStruct.new
18
+ @config = Vanilla::Config.new
19
+ end
20
+ def inherited(app)
21
+ Vanilla.apps << app
17
22
  end
18
23
  end
19
24
 
20
- attr_reader :request, :response, :config, :soup
21
-
22
- def class_config
23
- if self.class.config
24
- {:soup => self.class.config.soup,
25
- :soups => self.class.config.soups,
26
- :root => self.class.config.root,
27
- :root_snip => self.class.config.root_snip,
28
- :renderers => self.class.config.renderers,
29
- :default_layout_snip => self.class.config.default_layout_snip,
30
- :default_renderer => self.class.config.default_renderer}
31
- else
32
- {}
33
- end
25
+ attr_reader :request, :response, :soup
26
+
27
+ def initialize
28
+ @renderers = Hash.new { config.default_renderer }
29
+ @soup = prepare_soup
30
+ prepare_renderers
34
31
  end
35
32
 
36
- # Create a new Vanilla application
37
- # Configuration options:
38
- #
39
- # :soup - provide the path to the soup data
40
- # :soups - provide an array of paths to soup data
41
- # :root - the directory that the soup paths are relative to;
42
- # defaults to Dir.pwd
43
- # :renderers - a hash of names to classes
44
- # :default_renderer - the class to use when no renderer is provided;
45
- # defaults to 'Vanilla::Renderers::Base'
46
- # :default_layout_snip - the snip to use as a layout when rendering to HTML;
47
- # defaults to 'layout'
48
- # :root_snip - the snip to load for the root ('/') url;
49
- # defaults to 'start'
50
- def initialize(additional_configuration={})
51
- @config = class_config.merge(additional_configuration)
52
- @root_directory = @config[:root] || Dir.pwd
53
- if @config[:soup].nil? && @config[:soups].nil?
54
- @config[:soup] = File.expand_path("soup", @root_directory)
55
- end
56
- @soup = prepare_soup(config)
57
- prepare_renderers(config[:renderers])
33
+ def config
34
+ self.class.config
58
35
  end
59
36
 
60
37
  # Returns a Rack-appropriate 3-element array (via Rack::Response#finish)
@@ -64,7 +41,7 @@ module Vanilla
64
41
  @response = Rack::Response.new
65
42
 
66
43
  begin
67
- output = formatted_render(request.snip, request.part, request.format)
44
+ output = render_in_format(request.snip, request.part, request.format)
68
45
  rescue => e
69
46
  @response.status = 500
70
47
  output = e.to_s + e.backtrace.join("\n")
@@ -76,29 +53,14 @@ module Vanilla
76
53
  @response.finish # returns the array
77
54
  end
78
55
 
79
- def formatted_render(snip, part=nil, format=nil)
80
- case format
81
- when 'html', nil
82
- layout = layout_for(snip)
83
- if layout == snip
84
- "Rendering of the current layout would result in infinite recursion."
85
- else
86
- render(layout)
87
- end
88
- when 'raw', 'css', 'js'
89
- Renderers::Raw.new(self).render(snip, part)
90
- when 'text', 'atom', 'xml'
91
- render(snip, part)
92
- else
93
- raise "Unknown format '#{format}'"
94
- end
95
- end
96
-
97
56
  # render a snip using either the renderer given, or the renderer
98
57
  # specified by the snip's "render_as" property, or Render::Base
99
58
  # if nothing else is given.
59
+ #
60
+ # This method can be useful if a dynasnip or other part of the
61
+ # system needs to get a fully rendered version of a snip.
100
62
  def render(snip, part=:content, args=[], enclosing_snip=snip)
101
- rendering(snip) do |renderer|
63
+ rendering_and_handling_errors(snip) do |renderer|
102
64
  renderer.render(snip, part, args, enclosing_snip)
103
65
  end
104
66
  end
@@ -108,24 +70,12 @@ module Vanilla
108
70
  if snip
109
71
  find_renderer(snip.render_as || snip.extension)
110
72
  else
111
- default_renderer
73
+ config.default_renderer
112
74
  end
113
75
  end
114
76
 
115
77
  def default_layout_snip
116
- soup[config[:default_layout_snip] || 'layout']
117
- end
118
-
119
- def layout_for(snip)
120
- if snip
121
- renderer_for(snip).new(self).layout_for(snip)
122
- else
123
- default_layout_snip
124
- end
125
- end
126
-
127
- def snip(attributes)
128
- @soup << attributes
78
+ soup[config.default_layout_snip]
129
79
  end
130
80
 
131
81
  def register_renderer(klass, *types)
@@ -139,31 +89,15 @@ module Vanilla
139
89
 
140
90
  private
141
91
 
142
- def prepare_renderers(additional_renderers={})
143
- @renderers = Hash.new { config[:default_renderer] || Vanilla::Renderers::Base }
144
- @renderers.merge!({
145
- "base" => Vanilla::Renderers::Base,
146
- "markdown" => Vanilla::Renderers::Markdown,
147
- "bold" => Vanilla::Renderers::Bold,
148
- "erb" => Vanilla::Renderers::Erb,
149
- "rb" => Vanilla::Renderers::Ruby,
150
- "ruby" => Vanilla::Renderers::Ruby,
151
- "haml" => Vanilla::Renderers::Haml,
152
- "raw" => Vanilla::Renderers::Raw,
153
- "textile" => Vanilla::Renderers::Textile
154
- })
155
- additional_renderers.each { |name, klass| register_renderer(klass, name) } if additional_renderers
92
+ def prepare_renderers
93
+ config.renderers.each { |name, klass| register_renderer(klass, name) }
156
94
  end
157
95
 
158
96
  def find_renderer(name)
159
97
  @renderers[(name ? name.downcase : nil)]
160
98
  end
161
99
 
162
- def default_renderer
163
- @renderers[nil]
164
- end
165
-
166
- def rendering(snip)
100
+ def rendering_and_handling_errors(snip)
167
101
  renderer_instance = renderer_for(snip).new(self)
168
102
  yield renderer_instance
169
103
  rescue Exception => e
@@ -173,14 +107,40 @@ module Vanilla
173
107
  e.backtrace.join("\n").gsub("<", "&lt;").gsub(">", "&gt;") + "</pre>"
174
108
  end
175
109
 
176
- def prepare_soup(config)
177
- if config[:soups]
178
- backends = [config[:soups]].flatten.map do |path|
179
- ::Soup::Backends::FileBackend.new(File.expand_path(path, @root_directory))
110
+ def render_in_format(snip, part=nil, format=nil)
111
+ case format
112
+ when 'html', nil
113
+ layout = layout_for(snip)
114
+ if layout == snip
115
+ "Rendering of the current layout would result in infinite recursion."
116
+ else
117
+ render(layout)
118
+ end
119
+ when 'raw', 'css', 'js'
120
+ Renderers::Raw.new(self).render(snip, part)
121
+ when 'text', 'atom', 'xml'
122
+ render(snip, part)
123
+ else
124
+ raise "Unknown format '#{format}'"
125
+ end
126
+ end
127
+
128
+ def layout_for(snip)
129
+ if snip
130
+ renderer_for(snip).new(self).layout_for(snip)
131
+ else
132
+ default_layout_snip
133
+ end
134
+ end
135
+
136
+ def prepare_soup
137
+ if config.soups
138
+ backends = [config.soups].flatten.map do |path|
139
+ ::Soup::Backends::FileBackend.new(File.expand_path(path, config.root))
180
140
  end
181
141
  ::Soup.new(::Soup::Backends::MultiSoup.new(*backends))
182
142
  else
183
- ::Soup.new(::Soup::Backends::FileBackend.new(File.expand_path(config[:soup], @root_directory)))
143
+ raise "No soups defined!"
184
144
  end
185
145
  end
186
146
  end
@@ -0,0 +1,46 @@
1
+ require "vanilla"
2
+
3
+ module Vanilla
4
+
5
+ # Create a new Vanilla application
6
+ # Configuration options:
7
+ #
8
+ # :soup - provide the path to the soup data
9
+ # :soups - provide an array of paths to soup data
10
+ # :root - the directory that the soup paths are relative to;
11
+ # defaults to Dir.pwd
12
+ # :renderers - a hash of names to classes
13
+ # :default_renderer - the class to use when no renderer is provided;
14
+ # defaults to 'Vanilla::Renderers::Base'
15
+ # :default_layout_snip - the snip to use as a layout when rendering to HTML;
16
+ # defaults to 'layout'
17
+ # :root_snip - the snip to load for the root ('/') url;
18
+ # defaults to 'start'
19
+ class Config
20
+ attr_accessor :root,
21
+ :root_snip,
22
+ :soups,
23
+ :renderers,
24
+ :default_layout_snip,
25
+ :default_renderer
26
+
27
+ def initialize
28
+ @root = Dir.pwd
29
+ @root_snip = "start"
30
+ @soups = ["soups/base", "soups/system"]
31
+ @default_layout_snip = "layout"
32
+ @default_renderer = Vanilla::Renderers::Base
33
+ @renderers = {
34
+ "base" => Vanilla::Renderers::Base,
35
+ "markdown" => Vanilla::Renderers::Markdown,
36
+ "bold" => Vanilla::Renderers::Bold,
37
+ "erb" => Vanilla::Renderers::Erb,
38
+ "rb" => Vanilla::Renderers::Ruby,
39
+ "ruby" => Vanilla::Renderers::Ruby,
40
+ "haml" => Vanilla::Renderers::Haml,
41
+ "raw" => Vanilla::Renderers::Raw,
42
+ "textile" => Vanilla::Renderers::Textile
43
+ }
44
+ end
45
+ end
46
+ end
@@ -6,7 +6,7 @@ end
6
6
  def app(reload=false)
7
7
  if !@__vanilla_console_app || reload
8
8
  load "application.rb"
9
- @__vanilla_console_app = Application.new
9
+ @__vanilla_console_app = Vanilla.apps.first.new
10
10
  end
11
11
  @__vanilla_console_app
12
12
  end
@@ -3,7 +3,6 @@ require 'vanilla/snip_reference_parser'
3
3
  module Vanilla
4
4
  module Renderers
5
5
  class Base
6
- include Routes
7
6
 
8
7
  # Render a snip.
9
8
  def self.render(snip, part=:content)
@@ -20,11 +19,22 @@ module Vanilla
20
19
  @app = app
21
20
  end
22
21
 
23
- # defined for the routes
24
22
  def soup
25
23
  @app.soup
26
24
  end
27
25
 
26
+ def url_to(*args)
27
+ @app.url_to(*args)
28
+ end
29
+
30
+ def link_to(link_text, snip_name=link_text, part=nil)
31
+ if soup[snip_name]
32
+ %{<a href="#{url_to(snip_name, part)}">#{link_text}</a>}
33
+ else
34
+ %{<a class="missing" href="#{url_to(snip_name, part)}">#{link_text}</a>}
35
+ end
36
+ end
37
+
28
38
  def self.snip_regexp
29
39
  %r{(\{[\w\-_\d\.\"\' ]+( +[^\}.]+)?\})}
30
40
  end
@@ -13,6 +13,7 @@ module Vanilla
13
13
  determine_request_uri_parts
14
14
  end
15
15
 
16
+ # returns the parameters of the request, with every key as a symbol
16
17
  def params
17
18
  # Don't you just love how terse functional programming tends to look like maths?
18
19
  @symbolised_params ||= @rack_request.params.inject({}) { |p, (k,v)| p[k.to_sym] = v; p }
@@ -24,47 +25,21 @@ module Vanilla
24
25
  @app.soup[snip_name]
25
26
  end
26
27
 
27
- def cookies
28
- @rack_request.cookies
29
- end
30
-
31
- def ip
32
- @rack_request.env["REMOTE_ADDR"]
33
- end
34
-
35
- def session
36
- @rack_request.env["rack.session"]
28
+ def method_missing(name, *args)
29
+ if @rack_request.respond_to?(name)
30
+ @rack_request.send(name, *args)
31
+ else
32
+ super
33
+ end
37
34
  end
38
35
 
39
36
  private
40
37
 
41
38
  def determine_request_uri_parts
42
- @snip_name, @part, @format = request_uri_parts(@rack_request)
39
+ @snip_name, @part, @format = Vanilla::Routing.parse(@rack_request.path_info)
40
+ @snip_name ||= @app.config.root_snip
43
41
  @format ||= 'html'
44
42
  @method = (params.delete(:_method) || @rack_request.request_method).downcase
45
43
  end
46
-
47
- def uri_path(request)
48
- request.path_info
49
- end
50
-
51
- URL_ROOT = /\A\/?\Z/ # i.e. / or nothing
52
- URL_SNIP = /\A\/([\w\-\s]+)(\/|\.(\w+))?\Z/ # i.e. /start, /start.html
53
- URL_SNIP_AND_PART = /\A\/([\w\-\s]+)\/([\w\-\s]+)(\/|\.(\w+))?\Z/ # i.e. /blah/part, /blah/part.raw
54
-
55
- # Returns an array of the requested snip, part and format
56
- def request_uri_parts(request)
57
- case CGI.unescape(uri_path(request))
58
- when URL_ROOT
59
- [@app.config[:root_snip] || 'start', nil, 'html']
60
- when URL_SNIP
61
- [$1, nil, $3]
62
- when URL_SNIP_AND_PART
63
- [$1, $2, $4]
64
- else
65
- []
66
- end
67
- end
68
-
69
44
  end
70
45
  end