noumenon 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +15 -0
  2. data/features/applications/mounting_an_application.feature +42 -0
  3. data/features/asset_serving/from_a_repository.feature +1 -1
  4. data/features/generator/application_generator.feature +13 -0
  5. data/features/support/env.rb +1 -0
  6. data/features/template_rendering/static.feature +10 -0
  7. data/features/template_tags/template_attribute.feature +3 -0
  8. data/features/template_tags/textilize.feature +0 -1
  9. data/features/template_tags/theme_attribute.feature +28 -0
  10. data/generators/application/Rakefile +16 -0
  11. data/generators/application/application.gemspec +28 -0
  12. data/generators/application/application.rb +15 -0
  13. data/generators/application/features/example.feature +9 -0
  14. data/generators/application/features/support/content/assets/.gitkeep +0 -0
  15. data/generators/application/features/support/content/pages/index.yml +3 -0
  16. data/generators/application/features/support/env.rb +19 -0
  17. data/generators/application/features/support/theme/assets/style.css +1 -0
  18. data/generators/application/features/support/theme/layouts/default.nou.html +23 -0
  19. data/generators/application/features/support/theme/templates/default.nou.html +12 -0
  20. data/generators/application/features/support/theme/theme.yml +5 -0
  21. data/generators/application/load.rb +7 -0
  22. data/generators/application/version.rb +5 -0
  23. data/lib/noumenon.rb +17 -1
  24. data/lib/noumenon/cli.rb +52 -6
  25. data/lib/noumenon/core.rb +19 -29
  26. data/lib/noumenon/cucumber.rb +1 -0
  27. data/lib/noumenon/cucumber/application_steps.rb +9 -0
  28. data/{features/step_definitions → lib/noumenon/cucumber}/asset_steps.rb +0 -5
  29. data/{features/step_definitions → lib/noumenon/cucumber}/content_steps.rb +5 -0
  30. data/lib/noumenon/cucumber/generator_steps.rb +64 -0
  31. data/{features/step_definitions → lib/noumenon/cucumber}/liquid_steps.rb +0 -0
  32. data/{features/step_definitions → lib/noumenon/cucumber}/navigation_steps.rb +0 -0
  33. data/{features/step_definitions → lib/noumenon/cucumber}/request_steps.rb +4 -0
  34. data/{features/step_definitions → lib/noumenon/cucumber}/theme_steps.rb +6 -0
  35. data/lib/noumenon/spec/example_app.rb +8 -15
  36. data/lib/noumenon/template.rb +5 -5
  37. data/lib/noumenon/theme.rb +15 -1
  38. data/lib/noumenon/version.rb +1 -1
  39. data/noumenon.gemspec +1 -1
  40. data/spec/noumenon/template_spec.rb +19 -2
  41. data/spec/noumenon/theme_spec.rb +15 -0
  42. data/spec/noumenon_spec.rb +37 -0
  43. data/spec/spec_helper.rb +12 -2
  44. metadata +33 -19
  45. data/features/mounted_applications.feature +0 -30
  46. data/features/step_definitions/generator_steps.rb +0 -24
@@ -1,3 +1,18 @@
1
+ # 0.2.3
2
+
3
+ ## New Features
4
+
5
+ * Applications are now mounted by using the `Noumenon.mount` method, so that users won't be able to
6
+ break mounted apps by deleting or changing their content items.
7
+ * The application API is slightly more stable now.
8
+ * Templates allow global variables to be registered using `Noumenon::Template.register_global "foo", "bar"`.
9
+ * All templates now have a `theme` object available to them which gives access to metadata.
10
+
11
+ ## Bug Fixes
12
+
13
+ * Assets from the asset repository will now be served correctly. I'm not sure how that ever worked
14
+ previously.
15
+
1
16
  # 0.2.2
2
17
 
3
18
  ## New Features
@@ -0,0 +1,42 @@
1
+ Feature: Mounting an Application
2
+ As a site developer
3
+ I want to mount an application on a specific URL
4
+ So that my users can have extra stuff
5
+
6
+ Scenario: ExampleApp mounted at /example
7
+ Given I have mounted "Noumenon::Spec::ExampleApp" at "/example"
8
+ When I view "/example"
9
+ Then the page should load
10
+ And the headline should be "This is a mounted application"
11
+
12
+ Scenario: Providing options
13
+ Given I have mounted "Noumenon::Spec::ExampleApp" at "/example" with these options:
14
+ | message | Hello, world! |
15
+ When I view "/example"
16
+ Then the page should load
17
+ And the headline should be "Hello, world!"
18
+
19
+ Scenario: Handling URLs below the mount point
20
+ Given I have mounted "Noumenon::Spec::ExampleApp" at "/example"
21
+ When I view "/example/sub"
22
+ Then the page should load
23
+ And the headline should be "This is a sub-page"
24
+
25
+ Scenario: Rendering a content item below a mounted application
26
+ Given I have mounted "Noumenon::Spec::ExampleApp" at "/example"
27
+ And this content item exists at "/example/templated"
28
+ | template | default |
29
+ | title | Template |
30
+ And this template exists at "default.nou.html"
31
+ """
32
+ <h1>{{ title }}</h1>
33
+ """
34
+ When I view "/example/templated"
35
+ Then the page should load
36
+ And the headline should be "Template"
37
+
38
+ Scenario: Mounting an application at the root
39
+ Given I have mounted "Noumenon::Spec::ExampleApp" at "/"
40
+ When I view "/"
41
+ Then the page should load
42
+ And the headline should be "This is a mounted application"
@@ -2,7 +2,7 @@ Feature: Serving assets from the asset repository
2
2
  As a user
3
3
  I want to be able to upload assets to my site
4
4
  So that I can attach my own images to pages
5
-
5
+
6
6
  Scenario: The asset has been added
7
7
  Given I have uploaded this asset as "example.txt" to the asset repository
8
8
  """
@@ -0,0 +1,13 @@
1
+ Feature: Generating an application
2
+ As an application developer
3
+ I want to create a skeleton application
4
+ So that I can provide custom functionality for my client
5
+
6
+ @wip
7
+ Scenario: Running the generator
8
+ When I run `bundle exec ../../bin/noumenon application contact_form_plus`
9
+ Then a directory named "contact_form_plus" should exist
10
+ And the directory "contact_form_plus" should contain a skeleton application
11
+ And the application in "contact_form_plus" should be named "ContactFormPlus"
12
+ And the gem in "contact_form_plus" should be named "noumenon-contact_form_plus"
13
+ And the application in "contact_form_plus" should be configured for Cucumber
@@ -6,6 +6,7 @@ require File.join(File.dirname(__FILE__), '..', '..', 'lib/noumenon.rb')
6
6
 
7
7
  require 'aruba/cucumber'
8
8
  require 'capybara/cucumber'
9
+ require 'noumenon/cucumber'
9
10
  require 'rspec'
10
11
 
11
12
  require 'fileutils'
@@ -13,6 +13,7 @@ Feature: Rendering a static template
13
13
  | template | static |
14
14
  When I view "/static"
15
15
  Then the page should load
16
+ And the "Content-Type" header should be "text/html;charset=utf-8"
16
17
  And the headline should be "Static Template"
17
18
  And the body text should be "This is a static template which does not have any fields."
18
19
 
@@ -22,6 +23,7 @@ Feature: Rendering a static template
22
23
  | template | static |
23
24
  When I view "/static"
24
25
  Then the page should return a 500 error
26
+ And the "Content-Type" header should be "text/html;charset=utf-8"
25
27
  And the headline should be "Missing Template"
26
28
  And the body text should be "The template 'static' does not exist within the current theme."
27
29
 
@@ -34,10 +36,18 @@ Feature: Rendering a static template
34
36
  | key | value |
35
37
  When I view "/default"
36
38
  Then the page should load
39
+ And the "Content-Type" header should be "text/html;charset=utf-8"
37
40
  And the headline should be "Default Template"
38
41
 
39
42
  Scenario: Content item does not exist
40
43
  Given no content item exists at "/static"
41
44
  When I view "/static"
42
45
  Then the page should return a 404 error
46
+ And the "Content-Type" header should be "text/html;charset=utf-8"
47
+ And the headline should be "Page Not Found"
48
+
49
+ Scenario: No content repository was configured
50
+ Given no content repository has been configured
51
+ When I view "/static"
52
+ Then the page should return a 404 error
43
53
  And the headline should be "Page Not Found"
@@ -0,0 +1,3 @@
1
+ Feature: The theme_attribute tag
2
+ As a designer
3
+ I want to include attributes from my theme in template
@@ -3,7 +3,6 @@ Feature: Using Textile to format text
3
3
  I want to allow my users to use Textile
4
4
  So that they can't mess up the design too badly
5
5
 
6
- @wip
7
6
  Scenario: A textile block
8
7
  Given this template exists at "textile.nou.html"
9
8
  """
@@ -0,0 +1,28 @@
1
+ Feature: The theme object
2
+ As a designer
3
+ I want to include attributes from my theme in template
4
+ So that it still contains copyright details when distributed
5
+
6
+ Background:
7
+ Given I am using this theme
8
+ | name | Theme Test |
9
+ | copyright | Jon Wood, 2011 |
10
+ | author | Jon Wood |
11
+ And this content item exists at "/theme_attribute"
12
+ | template | theme_example |
13
+
14
+ Scenario: A defined theme attribute
15
+ Given this template exists at "theme_example.nou.html"
16
+ """
17
+ <h1>{{ theme.name }}</h1>
18
+ """
19
+ When I view "/theme_attribute"
20
+ Then the headline should be "Theme Test"
21
+
22
+ Scenario: An undefined theme attribute
23
+ Given this template exists at "theme_example.nou.html"
24
+ """
25
+ <h1>{{ theme.bob }}</h1>
26
+ """
27
+ When I view "/theme_attribute"
28
+ Then the headline should be ""
@@ -0,0 +1,16 @@
1
+ require 'cucumber/rake/task'
2
+ require 'bundler/gem_helper'
3
+
4
+ task :default => :cucumber
5
+
6
+ Cucumber::Rake::Task.new do |t|
7
+ t.cucumber_opts = %w(--format progress)
8
+ end
9
+
10
+ Cucumber::Rake::Task.new("cucumber:wip") do |t|
11
+ t.cucumber_opts = %w(--tags @wip:4 --wip)
12
+ end
13
+
14
+ namespace :gem do
15
+ Bundler::GemHelper.install_tasks
16
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "noumenon/<%= @name %>/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "noumenon-<%= @name %>"
7
+ s.version = Noumenon::<%= @name.split("_").collect(&:capitalize).join("") %>::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = [ "Jon Wood" ]
10
+ s.email = [ "jon@blankpad.net" ]
11
+ s.homepage = "https://github.com/Noumenon/<%= @name %>"
12
+ s.summary = %q{A <%= @name.gsub("_", " ") %> application for Noumenon.}
13
+ s.description = <<EOF
14
+ This is a description of your application. You should update it.
15
+ EOF
16
+
17
+ s.rubyforge_project = "noumenon-<%= @name %>"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency "noumenon"
24
+
25
+ s.add_development_dependency "cucumber", "~> 1.0.0"
26
+ s.add_development_dependency "capybara", "~> 1.0.0.rc1"
27
+ s.add_development_dependency "rspec", "~> 2.5"
28
+ end
@@ -0,0 +1,15 @@
1
+ require 'noumenon'
2
+
3
+ module Noumenon
4
+ module <%= @name.split("_").collect(&:capitalize).join("") %>
5
+ class Application < Noumenon::Core
6
+ def default_options
7
+ {}
8
+ end
9
+
10
+ get "/" do
11
+ "<h1>Welcome to <%= @name.gsub("_", " ").capitalize %></h1>"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ Feature: Loading the root
2
+ As a developer
3
+ I want to know that my features work
4
+ So that I can carry on work
5
+
6
+ Scenario: Loading the home page
7
+ When I view "/"
8
+ Then the page should load
9
+ And the headline should be "Welcome to <%= @name.gsub("_", " ").capitalize %>"
@@ -0,0 +1,3 @@
1
+ template: "default"
2
+ title: "Welcome to Noumenon"
3
+ body: "You've just created a Noumenon site, well done!"
@@ -0,0 +1,19 @@
1
+ require 'noumenon'
2
+ require File.expand_path('../../../lib/noumenon/<%= @name %>', __FILE__)
3
+
4
+ require 'rspec'
5
+ require 'capybara/cucumber'
6
+ require 'noumenon/cucumber'
7
+
8
+ World do
9
+ include RSpec::Expectations
10
+ include RSpec::Matchers
11
+
12
+ Noumenon.mount '/', Noumenon::<%= @name.split("_").collect(&:capitalize).join("") %>::Application
13
+
14
+ Noumenon.theme = Noumenon::Theme.load(File.expand_path("../theme", __FILE__))
15
+ Noumenon.content_repository = Noumenon::ContentRepository::FileSystem.new(path: File.expand_path("../content/pages"))
16
+ Noumenon.asset_repository = Noumenon::AssetRepository::FileSystem.new(path: File.expand_path("../content/assets"))
17
+
18
+ Capybara.app = Noumenon.server
19
+ end
@@ -0,0 +1 @@
1
+ /* Your themes styles should go here. */
@@ -0,0 +1,23 @@
1
+ title:
2
+ type: string
3
+ description: The title to put in the browser's title bar.
4
+ required: false
5
+
6
+ content:
7
+ type: string
8
+ description: The content from the page template.
9
+ required: true
10
+ ---
11
+ <!doctype html>
12
+ <html>
13
+ <head>
14
+ <title>{{ title }}</title>
15
+ <link rel="stylesheet" href="{% theme_asset style.css %}" />
16
+ </head>
17
+
18
+ <body>
19
+ <section id="content">
20
+ {{ content }}
21
+ </section>
22
+ </body>
23
+ </html>
@@ -0,0 +1,12 @@
1
+ title:
2
+ description: The title of the content block
3
+ required: true
4
+ type: string
5
+
6
+ body:
7
+ description: The main content
8
+ required: true
9
+ type: text
10
+ ---
11
+ <h1>{{ title }}</h1>
12
+ <p>{{ body }}</p>
@@ -0,0 +1,5 @@
1
+ name: "Generated Theme"
2
+ author: "Your Name"
3
+ email: "you@example.org"
4
+ copyright: "2011, Your Name"
5
+ license: "MIT"
@@ -0,0 +1,7 @@
1
+ require 'noumenon/<%= @name %>/version'
2
+
3
+ module Noumenon
4
+ module <%= @name.split("_").collect(&:capitalize).join("") %>
5
+ autoload :Application, 'noumenon/<%= @name %>/application'
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Noumenon
2
+ module <%= @name.split("_").collect(&:capitalize).join("") %>
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -29,7 +29,10 @@ module Noumenon
29
29
  def self.server
30
30
  Rack::Builder.new do
31
31
  use Noumenon::Theme::AssetsMiddleware
32
- run Noumenon::Core
32
+
33
+ Noumenon.mounted_applications.each do |path, app|
34
+ map(path) { run app[:application].new(app[:options].merge(mount_point: path)) }
35
+ end
33
36
  end
34
37
  end
35
38
 
@@ -92,4 +95,17 @@ module Noumenon
92
95
 
93
96
  @theme
94
97
  end
98
+
99
+ def self.mounted_applications
100
+ @mounted_applications ||= {
101
+ "/" => { application: Noumenon::Core, options: {} }
102
+ }
103
+ end
104
+
105
+ def self.mount(path, application, options = {})
106
+ mounted_applications[path] = {
107
+ application: application,
108
+ options: options
109
+ }
110
+ end
95
111
  end
@@ -7,21 +7,67 @@ class Noumenon::Cli < Thor
7
7
 
8
8
  desc "site PATH", "Generate a new site at PATH"
9
9
  def site(path)
10
- say_status :site, path
11
- directory "site", path
10
+ generate :site, path
12
11
  invoke :theme, [ File.join(path, "theme") ]
13
12
  invoke :repository, [ File.join(path, "content") ]
14
13
  end
15
14
 
16
15
  desc "theme PATH", "Generate a new theme at PATH"
17
16
  def theme(path)
18
- say_status :theme, path
19
- directory "theme", path
17
+ generate :theme, path
20
18
  end
21
19
 
22
20
  desc "repository PATH", "Generate a new content repository at PATH"
23
21
  def repository(path)
24
- say_status :repository, path
25
- directory "repository", path
22
+ generate :repository, path
23
+ end
24
+
25
+ desc "application NAME", "Generate a new application at NAME"
26
+ def application(name)
27
+ @name = name
28
+
29
+ say_status :application, name
30
+
31
+ empty_directory name
32
+
33
+ inside name do
34
+ [ "lib", "lib/noumenon", "lib/noumenon", "lib/noumenon/#{name}", "templates", "assets", "features", "features/support", "features/step_definitions" ].each do |dir|
35
+ empty_directory dir
36
+ end
37
+
38
+ create_file("Gemfile") { "gemspec" }
39
+ end
40
+
41
+ template "application/application.gemspec", "#{name}/noumenon-#{name}.gemspec"
42
+ template "application/application.rb", "#{name}/lib/noumenon/#{name}/application.rb"
43
+ template "application/version.rb", "#{name}/lib/noumenon/#{name}/version.rb"
44
+ template "application/load.rb", "#{name}/lib/noumenon/#{name}.rb"
45
+
46
+ invoke :application_features, name
47
+ end
48
+
49
+ desc "application_features NAME", "Generate Cucumber configuration for the application at NAME"
50
+ def application_features(name)
51
+ @name = name
52
+
53
+ inside name do
54
+ [ "features", "features/support", "features/step_definitions" ].each do |dir|
55
+ empty_directory dir
56
+ end
57
+ end
58
+
59
+ template "application/Rakefile", "#{name}/Rakefile"
60
+ template "application/features/example.feature", "#{name}/features/example.feature"
61
+ template "application/features/support/env.rb", "#{name}/features/support/env.rb"
62
+
63
+ path = "#{name}/features/support"
64
+ invoke :theme, [ File.join(path, "theme") ]
65
+ invoke :repository, [ File.join(path, "content") ]
66
+ end
67
+
68
+ protected
69
+ def generate(thing, path)
70
+ say_status thing.to_sym, path
71
+ directory thing.to_s, path
26
72
  end
27
73
  end
@@ -8,6 +8,17 @@ require 'rack/mime'
8
8
  #
9
9
  # @api public
10
10
  class Noumenon::Core < Sinatra::Base
11
+ def default_options
12
+ {}
13
+ end
14
+
15
+ def initialize(options = {})
16
+ @options = default_options.merge(options.symbolize_keys)
17
+ @options[:mount_point] ||= "/"
18
+
19
+ super nil
20
+ end
21
+
11
22
  # Renders a content item within it's template and layout.
12
23
  #
13
24
  # If the template or layout is not specified then it will be set to "default".
@@ -46,44 +57,23 @@ class Noumenon::Core < Sinatra::Base
46
57
  # sending it back out again. In the future asset repositories will be able to implement more sensible
47
58
  # methods of sending content, such as redirecting to an S3 URL.
48
59
  #
49
- # TODO: Abstract /assets/ out to a middleware which can be implemented by repositories which
60
+ # TODO: Abstract /assets/ out to an application which can be implemented by repositories which
50
61
  # can't just provide a URL straight up (such as MongoDB).
51
62
  get "/assets/*" do |path|
52
- asset = Noumenon.asset_repository.get(path)
63
+ asset = Noumenon.asset_repository.get_asset_path(path)
53
64
  halt 404, "Asset not found" unless asset
54
65
 
55
66
  send_file asset
56
67
  end
57
68
 
58
- get "*" do |path|
59
- page = content.get(path)
60
-
61
- unless page
62
- # Search up the tree until we either hit the top, and 404, or find a mounted application.
63
- path = path.split("/")
64
- while path.size > 0
65
- path.pop
66
- page = content.get(path.join("/"))
67
- break if page && page[:type] == "application"
68
- end
69
-
70
- halt 404, "<h1>Page Not Found</h1>" unless page
71
- path = path.join("/")
72
- end
73
-
74
- case page[:type]
75
- when "application"
76
- app = page[:application].constantize
77
-
78
- # Rewrite PATH_INFO so that the child application can listen to URLs assuming it's at the root.
79
- env["PATH_INFO"].gsub!("#{path}", "")
80
- env["PATH_INFO"] = "/" if env["PATH_INFO"] == ""
81
- app.new(page).call(env)
82
- else
69
+ def route_missing
70
+ if content && page = content.get(File.join(@options[:mount_point], @request.path_info))
83
71
  render_page(page)
84
- end
72
+ else
73
+ halt 404, "<h1>Page Not Found</h1>"
74
+ end
85
75
  end
86
-
76
+
87
77
  private
88
78
  def wrap_with_layout(content, page)
89
79
  begin
@@ -0,0 +1 @@
1
+ Dir.glob(File.expand_path("../cucumber/**/*.rb", __FILE__)).each { |f| require f }
@@ -0,0 +1,9 @@
1
+ Given /^I have mounted "([^"]*)" at "([^"]*)" with these options:$/ do |application, path, options|
2
+ Noumenon.mount(path, application.constantize, options.rows_hash.symbolize_keys)
3
+ Capybara.app = Noumenon.server
4
+ end
5
+
6
+ Given /^I have mounted "([^"]*)" at "([^"]*)"$/ do |application, path|
7
+ Noumenon.mount(path, application.constantize)
8
+ Capybara.app = Noumenon.server
9
+ end
@@ -6,11 +6,6 @@ Given /^the asset "([^"]*)" exists within the theme with this content:$/ do |ass
6
6
  File.open(File.join(Noumenon.theme.path, "assets", asset_name), "w") { |f| f.print content }
7
7
  end
8
8
 
9
- Given /^I have chosen to use a seperate asset repository$/ do
10
- FileUtils.mkdir_p File.join(tmp_path, "assets")
11
- Noumenon.asset_repository = Noumenon::ContentRepository::FileSystem.new(path: File.join(tmp_path, "assets"))
12
- end
13
-
14
9
  Given /^I have uploaded this asset as "([^"]*)" to the asset repository$/ do |path, string|
15
10
  Noumenon.asset_repository.put(path, string)
16
11
  end
@@ -1,3 +1,8 @@
1
+ Given /^no content repository has been configured$/ do
2
+ Noumenon.content_repository = nil
3
+ Capybara.app = Noumenon.server
4
+ end
5
+
1
6
  Given /^this content item exists at "([^"]*)"$/ do |path, fields|
2
7
  Noumenon.content_repository.put(path, fields.rows_hash)
3
8
  end
@@ -0,0 +1,64 @@
1
+ def generator_file(generator, file)
2
+ File.read File.expand_path("../../../../generators/#{generator}/#{file}", __FILE__)
3
+ end
4
+
5
+ Then /^the directory "([^"]*)" should contain a Noumenon site$/ do |path|
6
+ steps %Q{
7
+ Then a file named "#{path}/Gemfile" should exist
8
+ Then a file named "#{path}/config.ru" should exist
9
+ }
10
+ check_exact_file_content "#{path}/config.ru", generator_file("site", "config.ru")
11
+ end
12
+
13
+ Then /^the directory "([^"]*)" should contain a theme$/ do |path|
14
+ check_exact_file_content "#{path}/theme.yml", generator_file("theme", "theme.yml")
15
+ check_exact_file_content "#{path}/layouts/default.nou.html", generator_file("theme", "layouts/default.nou.html")
16
+ check_exact_file_content "#{path}/templates/default.nou.html", generator_file("theme", "templates/default.nou.html")
17
+ check_exact_file_content "#{path}/assets/style.css", generator_file("theme", "assets/style.css")
18
+ end
19
+
20
+ Then /^the directory "([^"]*)" should contain a repository$/ do |path|
21
+ steps %Q{
22
+ Then a directory named "#{path}/pages" should exist
23
+ And a directory named "#{path}/assets" should exist
24
+ }
25
+
26
+ check_exact_file_content "#{path}/pages/index.yml", generator_file("repository/pages", "index.yml")
27
+ end
28
+
29
+ Then /^the directory "([^"]*)" should contain a skeleton application$/ do |path|
30
+ steps %Q{
31
+ Then a file named "#{path}/Gemfile" should exist
32
+ And a file named "#{path}/noumenon-#{path}.gemspec" should exist
33
+ And a file named "#{path}/lib/noumenon/#{path}.rb" should exist
34
+ And a directory named "#{path}/lib/noumenon/#{path}/" should exist
35
+ And a file named "#{path}/lib/noumenon/#{path}/version.rb" should exist
36
+ And a file named "#{path}/lib/noumenon/#{path}/application.rb" should exist
37
+ And a directory named "#{path}/templates/" should exist
38
+ And a directory named "#{path}/assets/" should exist
39
+ }
40
+ end
41
+
42
+ Then /^the application in "([^"]*)" should be configured for Cucumber$/ do |path|
43
+ steps %Q{
44
+ Then a file named "#{path}/Rakefile" should exist
45
+ And a directory named "#{path}/features" should exist
46
+ And a directory named "#{path}/features/support" should exist
47
+ And a file named "#{path}/features/support/env.rb" should exist
48
+ And the directory "#{path}/features/support/content" should contain a repository
49
+ And the directory "#{path}/features/support/theme" should contain a theme
50
+ And a directory named "#{path}/features/step_definitions" should exist
51
+ }
52
+ end
53
+
54
+ Then /^the application in "([^"]*)" should be named "([^"]*)"$/ do |path, name|
55
+ in_current_dir do
56
+ File.read("#{path}/lib/noumenon/#{path}.rb").should =~ /module #{name}/
57
+ end
58
+ end
59
+
60
+ Then /^the gem in "([^"]*)" should be named "([^"]*)"$/ do |path, name|
61
+ in_current_dir do
62
+ File.read("#{path}/#{name}.gemspec").should =~ /s\.name\s+= "#{name}"/
63
+ end
64
+ end
@@ -10,6 +10,10 @@ Then /^the page should return a (\d+) error$/ do |status|
10
10
  page.status_code.should == status.to_i
11
11
  end
12
12
 
13
+ Then /^the "([^"]*)" header should be "([^"]*)"$/ do |header, value|
14
+ page.response_headers[header].should == value
15
+ end
16
+
13
17
  Then /^the page content should be:$/ do |content|
14
18
  page.source.should == content
15
19
  end
@@ -1,3 +1,9 @@
1
+ Given /^I am using this theme$/ do |attributes|
2
+ create_theme attributes.rows_hash
3
+ Noumenon.theme = Noumenon::Theme.load(theme_path)
4
+ Capybara.app = Noumenon.server
5
+ end
6
+
1
7
  Given /^this template exists at "([^"]*)"$/ do |path, template|
2
8
  File.open(File.join(Noumenon.theme.path, "templates", path), "w") do |f|
3
9
  f.write(template)
@@ -1,22 +1,15 @@
1
1
  require 'noumenon'
2
2
 
3
- class Noumenon::Spec::ExampleApp
4
- def initialize(options = {})
5
- @options = { message: "This is a mounted application" }.merge(options.symbolize_keys)
3
+ class Noumenon::Spec::ExampleApp < Noumenon::Core
4
+ def default_options
5
+ { message: "This is a mounted application" }
6
6
  end
7
7
 
8
- def call(env)
9
- case env["PATH_INFO"]
10
- when "/"
11
- respond 200, "<h1>#{@options[:message]}</h1>"
12
- when "/sub"
13
- respond 200, "<h1>This is a sub-page</h1>"
14
- else
15
- respond 404, "Page Not Found"
16
- end
8
+ get "/" do
9
+ "<h1>#{@options[:message]}</h1>"
17
10
  end
18
-
19
- def respond(status, content)
20
- [ status, { "Content-Type" => "text/html" }, StringIO.new(content) ]
11
+
12
+ get "/sub" do
13
+ "<h1>This is a sub-page</h1>"
21
14
  end
22
15
  end
@@ -86,12 +86,12 @@ class Noumenon::Template
86
86
  self.new(path, content, fields)
87
87
  end
88
88
 
89
- def self.drops
90
- @drops ||= {}
89
+ def self.globals
90
+ @globals ||= {}
91
91
  end
92
92
 
93
- def self.register_drop(name, klass)
94
- drops[name] = klass
93
+ def self.register_global(name, klass)
94
+ globals[name] = klass
95
95
  end
96
96
 
97
97
  # Creates a new Template instance.
@@ -132,7 +132,7 @@ class Noumenon::Template
132
132
 
133
133
  raise MissingContentError.new("The following fields were missing from your content: #{missing_fields.sort.join(", ")}") unless missing_fields.empty?
134
134
 
135
- Liquid::Template.parse(content).render(fields_from_page.merge(self.class.drops))
135
+ Liquid::Template.parse(content).render(fields_from_page.merge(self.class.globals))
136
136
  end
137
137
  end
138
138
 
@@ -1,5 +1,6 @@
1
1
  require 'noumenon'
2
2
  require 'yaml'
3
+ require 'liquid/drop'
3
4
 
4
5
  # Provides access to a theme and it's contents.
5
6
  #
@@ -64,7 +65,7 @@ class Noumenon::Theme
64
65
  # @api public
65
66
  attr_accessor :license
66
67
 
67
- # Create a new them instance.
68
+ # Create a new theme instance.
68
69
  #
69
70
  # @param [ String, #to_s ] path The path the theme was loaded from.
70
71
  # @param [ Hash ] description Any metadata to attach to theme.
@@ -103,4 +104,17 @@ class Noumenon::Theme
103
104
  def layout(name)
104
105
  Noumenon::Template.from_file File.join(path, "layouts", name)
105
106
  end
107
+
108
+ class Drop < ::Liquid::Drop
109
+ def before_method(method)
110
+ @theme ||= Noumenon.theme
111
+ @theme.to_liquid[method.to_sym]
112
+ end
113
+ end
114
+
115
+ def to_liquid
116
+ { name: name, author: author, email: email, copyright: copyright, license: license }
117
+ end
118
+
119
+ Noumenon::Template.register_global 'theme', Drop.new
106
120
  end
@@ -1,5 +1,5 @@
1
1
  module Noumenon
2
2
  # The current version of Noumenon.
3
3
  # @api public
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
@@ -39,7 +39,7 @@ EOF
39
39
 
40
40
  s.add_development_dependency "aruba", "~> 0.3"
41
41
  s.add_development_dependency "capybara", "~> 1.0.0.beta1"
42
- s.add_development_dependency "cucumber", "~> 0.10"
42
+ s.add_development_dependency "cucumber", "~> 1.0"
43
43
  s.add_development_dependency "rake", "~> 0.9"
44
44
  s.add_development_dependency "rdiscount", "~> 1.6"
45
45
  s.add_development_dependency "rspec", "~> 2.5"
@@ -71,7 +71,7 @@ describe Noumenon::Template do
71
71
  end
72
72
  end
73
73
  end
74
-
74
+
75
75
  describe "rendering a template" do
76
76
  let(:template) { Noumenon::Template.from_file template_path("template_with_fields.html") }
77
77
 
@@ -94,7 +94,6 @@ describe Noumenon::Template do
94
94
  context "when all required fields were provided" do
95
95
  it "renders the template, replacing any fields" do
96
96
  content = template.render("title" => "Example Page", "body" => "This is an example, isn't it lovely.", "author" => "Jon Wood")
97
-
98
97
  content.should =~ %r{<h1>Example Page</h1>}
99
98
  content.should =~ %r{<div id="content">This is an example, isn't it lovely.</div>}
100
99
  content.should =~ %r{<p class="byline">Written by Jon Wood.</p>}
@@ -117,4 +116,22 @@ describe Noumenon::Template do
117
116
  end
118
117
  end
119
118
  end
119
+
120
+ describe "registering global variables" do
121
+ subject { Noumenon::Template }
122
+
123
+ it { should respond_to :globals }
124
+ it { should respond_to :register_global }
125
+
126
+ it "adds the global to the list" do
127
+ Noumenon::Template.register_global "foo", "bar"
128
+ Noumenon::Template.globals["foo"].should == "bar"
129
+ end
130
+
131
+ it "includes the variable when rendering a template" do
132
+ Noumenon::Template.register_global "foo", "bar"
133
+
134
+ Noumenon::Template.new("#{__FILE__}: #{__LINE__}", "{{ foo }}").render.should == "bar"
135
+ end
136
+ end
120
137
  end
@@ -125,5 +125,20 @@ describe Noumenon::Theme do
125
125
  end
126
126
  end
127
127
  end
128
+
129
+ describe "making themes available to a Liquid template" do
130
+ before(:each) { Noumenon.theme = theme }
131
+ subject { theme.to_liquid }
132
+
133
+ it { should be_instance_of Hash }
134
+
135
+ %w(name author email copyright license).each do |attribute|
136
+ specify { subject[attribute.to_sym].should == theme.send(attribute) }
137
+ end
138
+
139
+ it "registers the current theme as a global variable for templates" do
140
+ Noumenon::Template.globals["theme"].should be_instance_of Noumenon::Theme::Drop
141
+ end
142
+ end
128
143
  end
129
144
  end
@@ -66,4 +66,41 @@ describe Noumenon do
66
66
  end
67
67
  end
68
68
  end
69
+
70
+ describe "mounting an application", capybara: true do
71
+ before do
72
+ Noumenon.mount "/app", Noumenon::Spec::ExampleApp
73
+ reload_server!
74
+ end
75
+
76
+ it "adds the application to the list of mounted apps" do
77
+ Noumenon.mounted_applications["/app"].should == {
78
+ application: Noumenon::Spec::ExampleApp,
79
+ options: {}
80
+ }
81
+ end
82
+
83
+ it "responds to requests on the path with the mounted application" do
84
+ visit '/app'
85
+
86
+ page.status_code.should == 200
87
+ page.should have_css "h1", text: "This is a mounted application"
88
+ end
89
+
90
+ it "passes any options through to the application" do
91
+ Noumenon.mount "/app", Noumenon::Spec::ExampleApp, message: "Hello, world!"
92
+ reload_server!
93
+
94
+ visit '/app'
95
+
96
+ page.status_code.should == 200
97
+ page.should have_css "h1", text: "Hello, world!"
98
+ end
99
+
100
+ it "allows sub-URLs to be handled by the application also" do
101
+ visit '/app/sub'
102
+
103
+ page.should have_css "h1", text: "This is a sub-page"
104
+ end
105
+ end
69
106
  end
@@ -1,12 +1,22 @@
1
+ ENV["RACK_ENV"] ||= "test"
2
+
1
3
  # Load anything in spec/support
2
4
  Dir.glob(File.join(File.expand_path("..", __FILE__), "support", "**", "*.rb")).each { |f| require f }
3
5
 
4
6
  require 'noumenon'
7
+ require 'capybara/dsl'
5
8
 
6
9
  RSpec.configure do |spec|
7
10
  include Noumenon::Spec::ThemeHelpers
11
+
12
+ # Capybara stuff only gets enabled when requested, otherwise Liquid breaks in obscure ways.
13
+ def reload_server!
14
+ Capybara.app = Noumenon.server
15
+ end
8
16
 
9
- spec.before(:each) do
10
- # Configure Noumenon
17
+ spec.include Capybara::DSL, capybara: true
18
+
19
+ spec.before(:each, capybara: true) do
20
+ reload_server!
11
21
  end
12
22
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: noumenon
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.2
5
+ version: 0.2.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jon Wood
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-11 00:00:00 +01:00
13
+ date: 2011-06-22 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -98,7 +98,7 @@ dependencies:
98
98
  requirements:
99
99
  - - ~>
100
100
  - !ruby/object:Gem::Version
101
- version: "0.10"
101
+ version: "1.0"
102
102
  type: :development
103
103
  version_requirements: *id008
104
104
  - !ruby/object:Gem::Dependency
@@ -176,17 +176,11 @@ files:
176
176
  - README.md
177
177
  - Rakefile
178
178
  - bin/noumenon
179
+ - features/applications/mounting_an_application.feature
179
180
  - features/asset_serving/from_a_repository.feature
180
181
  - features/asset_serving/from_themes.feature
182
+ - features/generator/application_generator.feature
181
183
  - features/generator/site_generator.feature
182
- - features/mounted_applications.feature
183
- - features/step_definitions/asset_steps.rb
184
- - features/step_definitions/content_steps.rb
185
- - features/step_definitions/generator_steps.rb
186
- - features/step_definitions/liquid_steps.rb
187
- - features/step_definitions/navigation_steps.rb
188
- - features/step_definitions/request_steps.rb
189
- - features/step_definitions/theme_steps.rb
190
184
  - features/support/env.rb
191
185
  - features/support/theme/theme.yml
192
186
  - features/template_extensions.feature
@@ -196,7 +190,22 @@ files:
196
190
  - features/template_tags/assign_to.feature
197
191
  - features/template_tags/content_from.feature
198
192
  - features/template_tags/nav_menu.feature
193
+ - features/template_tags/template_attribute.feature
199
194
  - features/template_tags/textilize.feature
195
+ - features/template_tags/theme_attribute.feature
196
+ - generators/application/Rakefile
197
+ - generators/application/application.gemspec
198
+ - generators/application/application.rb
199
+ - generators/application/features/example.feature
200
+ - generators/application/features/support/content/assets/.gitkeep
201
+ - generators/application/features/support/content/pages/index.yml
202
+ - generators/application/features/support/env.rb
203
+ - generators/application/features/support/theme/assets/style.css
204
+ - generators/application/features/support/theme/layouts/default.nou.html
205
+ - generators/application/features/support/theme/templates/default.nou.html
206
+ - generators/application/features/support/theme/theme.yml
207
+ - generators/application/load.rb
208
+ - generators/application/version.rb
200
209
  - generators/repository/assets/.gitkeep
201
210
  - generators/repository/pages/index.yml
202
211
  - generators/site/Gemfile
@@ -212,6 +221,15 @@ files:
212
221
  - lib/noumenon/content_repository.rb
213
222
  - lib/noumenon/content_repository/file_system.rb
214
223
  - lib/noumenon/core.rb
224
+ - lib/noumenon/cucumber.rb
225
+ - lib/noumenon/cucumber/application_steps.rb
226
+ - lib/noumenon/cucumber/asset_steps.rb
227
+ - lib/noumenon/cucumber/content_steps.rb
228
+ - lib/noumenon/cucumber/generator_steps.rb
229
+ - lib/noumenon/cucumber/liquid_steps.rb
230
+ - lib/noumenon/cucumber/navigation_steps.rb
231
+ - lib/noumenon/cucumber/request_steps.rb
232
+ - lib/noumenon/cucumber/theme_steps.rb
215
233
  - lib/noumenon/spec.rb
216
234
  - lib/noumenon/spec/example_app.rb
217
235
  - lib/noumenon/spec/example_tags.rb
@@ -264,17 +282,11 @@ signing_key:
264
282
  specification_version: 3
265
283
  summary: A flexible content management system.
266
284
  test_files:
285
+ - features/applications/mounting_an_application.feature
267
286
  - features/asset_serving/from_a_repository.feature
268
287
  - features/asset_serving/from_themes.feature
288
+ - features/generator/application_generator.feature
269
289
  - features/generator/site_generator.feature
270
- - features/mounted_applications.feature
271
- - features/step_definitions/asset_steps.rb
272
- - features/step_definitions/content_steps.rb
273
- - features/step_definitions/generator_steps.rb
274
- - features/step_definitions/liquid_steps.rb
275
- - features/step_definitions/navigation_steps.rb
276
- - features/step_definitions/request_steps.rb
277
- - features/step_definitions/theme_steps.rb
278
290
  - features/support/env.rb
279
291
  - features/support/theme/theme.yml
280
292
  - features/template_extensions.feature
@@ -284,7 +296,9 @@ test_files:
284
296
  - features/template_tags/assign_to.feature
285
297
  - features/template_tags/content_from.feature
286
298
  - features/template_tags/nav_menu.feature
299
+ - features/template_tags/template_attribute.feature
287
300
  - features/template_tags/textilize.feature
301
+ - features/template_tags/theme_attribute.feature
288
302
  - spec/noumenon/asset_repository/file_system_spec.rb
289
303
  - spec/noumenon/asset_repository_spec.rb
290
304
  - spec/noumenon/content_repository/file_system_spec.rb
@@ -1,30 +0,0 @@
1
- Feature: Mounted Applications
2
- As a developer
3
- I want to mount an application at /app
4
- So that I can provide extra functionality
5
- In order to deliver the site my client wants
6
-
7
- Scenario: An application has been mounted
8
- Given this content item exists at "/app"
9
- | type | application |
10
- | application | Noumenon::Spec::ExampleApp |
11
- When I view "/app"
12
- Then the page should load
13
- And the headline should be "This is a mounted application"
14
-
15
- Scenario: Loading paths below the mounted application
16
- Given this content item exists at "/app"
17
- | type | application |
18
- | application | Noumenon::Spec::ExampleApp |
19
- When I view "/app/sub"
20
- Then the page should load
21
- And the headline should be "This is a sub-page"
22
-
23
- Scenario: Passing configuration to an application
24
- Given this content item exists at "/app"
25
- | type | application |
26
- | application | Noumenon::Spec::ExampleApp |
27
- | message | Hello, world! |
28
- When I view "/app"
29
- Then the page should load
30
- And the headline should be "Hello, world!"
@@ -1,24 +0,0 @@
1
- def generator_file(generator, file)
2
- File.read File.expand_path("../../../generators/#{generator}/#{file}", __FILE__)
3
- end
4
-
5
- Then /^the directory "([^"]*)" should contain a Noumenon site$/ do |path|
6
- Then %Q{a file named "#{path}/Gemfile" should exist}
7
- And %Q{a file named "#{path}/config.ru" should exist}
8
-
9
- check_exact_file_content "#{path}/Gemfile", generator_file("site", "Gemfile")
10
- check_exact_file_content "#{path}/config.ru", generator_file("site", "config.ru")
11
- end
12
-
13
- Then /^the directory "([^"]*)" should contain a theme$/ do |path|
14
- check_exact_file_content "#{path}/theme.yml", generator_file("theme", "theme.yml")
15
- check_exact_file_content "#{path}/layouts/default.nou.html", generator_file("theme", "layouts/default.nou.html")
16
- check_exact_file_content "#{path}/templates/default.nou.html", generator_file("theme", "templates/default.nou.html")
17
- check_exact_file_content "#{path}/assets/style.css", generator_file("theme", "assets/style.css")
18
- end
19
-
20
- Then /^the directory "([^"]*)" should contain a repository$/ do |path|
21
- Then %Q{a directory named "#{path}/pages" should exist}
22
- check_exact_file_content "#{path}/pages/index.yml", generator_file("repository/pages", "index.yml")
23
- And %Q{a directory named "#{path}/assets" should exist}
24
- end