noumenon 0.2.2 → 0.2.3

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 (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