noumenon 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +0 -4
  3. data/.yardopts +5 -0
  4. data/Gemfile +1 -3
  5. data/README.md +57 -80
  6. data/Rakefile +17 -6
  7. data/bin/noumenon +6 -0
  8. data/features/dynamic_template_rendering.feature +107 -0
  9. data/features/generator/site_generator.feature +25 -0
  10. data/features/mounted_applications.feature +30 -0
  11. data/features/static_template_rendering.feature +43 -0
  12. data/features/step_definitions/asset_steps.rb +7 -0
  13. data/features/step_definitions/content_steps.rb +7 -0
  14. data/features/step_definitions/generator_steps.rb +22 -0
  15. data/features/step_definitions/request_steps.rb +31 -0
  16. data/features/step_definitions/theme_steps.rb +19 -0
  17. data/features/support/env.rb +38 -0
  18. data/features/support/theme/theme.yml +5 -0
  19. data/features/theme_assets.feature +22 -0
  20. data/generators/repository/index.yml +3 -0
  21. data/generators/site/Gemfile +3 -0
  22. data/generators/site/config.ru +7 -0
  23. data/generators/theme/assets/style.css +1 -0
  24. data/generators/theme/layouts/default.nou.html +23 -0
  25. data/generators/theme/templates/default.nou.html +12 -0
  26. data/generators/theme/theme.yml +5 -0
  27. data/lib/noumenon/cli.rb +27 -0
  28. data/lib/noumenon/core.rb +70 -77
  29. data/lib/noumenon/repository/file_system.rb +102 -0
  30. data/lib/noumenon/repository.rb +39 -0
  31. data/lib/noumenon/spec/example_app.rb +19 -6
  32. data/lib/noumenon/spec/theme_helpers.rb +66 -0
  33. data/lib/noumenon/spec.rb +4 -7
  34. data/lib/noumenon/string_extensions.rb +21 -0
  35. data/lib/noumenon/template.rb +113 -72
  36. data/lib/noumenon/theme/assets_middleware.rb +21 -0
  37. data/lib/noumenon/theme.rb +106 -0
  38. data/lib/noumenon/version.rb +3 -1
  39. data/lib/noumenon.rb +68 -100
  40. data/noumenon.gemspec +13 -9
  41. data/spec/noumenon/repository/file_system_spec.rb +115 -0
  42. data/spec/noumenon/repository_spec.rb +40 -0
  43. data/spec/noumenon/template_spec.rb +9 -7
  44. data/spec/noumenon/theme_spec.rb +129 -0
  45. data/spec/noumenon_spec.rb +24 -80
  46. data/spec/spec_helper.rb +5 -14
  47. data/spec/support/file_matchers.rb +45 -0
  48. data/spec/support/templates/basic_template.html +1 -0
  49. data/spec/{fixtures/themes/example_without_layout → support}/templates/template_with_fields.html +1 -1
  50. metadata +143 -62
  51. data/lib/noumenon/configuration.rb +0 -28
  52. data/lib/noumenon/spec/fixtures.rb +0 -34
  53. data/spec/fixtures/fixture_specs/test +0 -1
  54. data/spec/fixtures/missing_application/mounted_app/config.yml +0 -1
  55. data/spec/fixtures/static_example/directory_with_index/index.yml +0 -1
  56. data/spec/fixtures/static_example/found.html +0 -1
  57. data/spec/fixtures/static_example/liquid_example.html +0 -1
  58. data/spec/fixtures/static_example/mounted_app/config.yml +0 -1
  59. data/spec/fixtures/static_example/template_with_substitutions.html +0 -1
  60. data/spec/fixtures/static_example/templates/basic_example.yml +0 -1
  61. data/spec/fixtures/static_example/templates/with_fields.yml +0 -2
  62. data/spec/fixtures/themes/example/assets/example.txt +0 -1
  63. data/spec/fixtures/themes/example/templates/layout.html +0 -3
  64. data/spec/fixtures/themes/example_without_layout/templates/basic_template.html +0 -1
  65. data/spec/fixtures/themes/example_without_layout/templates/fields.html +0 -1
  66. data/spec/fixtures/themes/external_theme/assets/example.txt +0 -1
  67. data/spec/fixtures/themes/unregistered/lib/unregistered.rb +0 -1
  68. data/spec/noumenon/config_spec.rb +0 -29
  69. data/spec/noumenon/core_spec.rb +0 -105
  70. data/spec/noumenon/spec/example_app_spec.rb +0 -14
  71. data/spec/noumenon/spec/fixtures_spec.rb +0 -41
  72. data/spec/noumenon/spec_spec.rb +0 -7
  73. data/watchr.rb +0 -2
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Noumenon::Repository do
4
+ describe "initialization" do
5
+ it "allows access to any provided options in #options" do
6
+ repo = Noumenon::Repository.new(foo: "bar")
7
+ repo.options[:foo].should == "bar"
8
+ end
9
+ end
10
+
11
+ it { should respond_to(:put) }
12
+ describe "calling #put" do
13
+ it "raises a NotImplementedError" do
14
+ lambda { subject.put("foo/bar", "Content") }.should raise_error NotImplementedError
15
+ end
16
+
17
+ it "provides some details in the error message" do
18
+ begin
19
+ subject.put("foo/bar", "Content")
20
+ rescue NotImplementedError => e
21
+ e.to_s.should == "This repository type does not support updating it's contents."
22
+ end
23
+ end
24
+ end
25
+
26
+ it { should respond_to(:get) }
27
+ describe "calling #get" do
28
+ it "raises a NotImplementedError" do
29
+ lambda { subject.get("foo/bar") }.should raise_error NotImplementedError
30
+ end
31
+
32
+ it "provides some details in the error message" do
33
+ begin
34
+ subject.get("foo/bar")
35
+ rescue NotImplementedError => e
36
+ e.to_s.should == "This repository type does not support reading it's contents."
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,20 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Noumenon::Template do
4
- it { should_not be_nil }
5
-
6
4
  def template_path(name)
7
- Noumenon::Core.new.theme_path("templates/#{name}")
5
+ File.expand_path("../../support/templates/#{name}", __FILE__)
8
6
  end
9
7
 
10
- before(:each) do
11
- Noumenon.config.theme = "example_without_layout"
12
- end
8
+ it { should_not be_nil }
13
9
 
14
10
  it { should respond_to(:fields) }
15
11
  it { should respond_to(:source) }
16
12
  it { should respond_to(:content) }
17
-
13
+
18
14
  describe "loading a template from disk" do
19
15
  it "raises a TemplateNotFoundError if the template does not exist" do
20
16
  lambda { Noumenon::Template.from_file "dummy" }.should raise_error Noumenon::Template::NotFoundError
@@ -103,6 +99,12 @@ describe Noumenon::Template do
103
99
  content.should =~ %r{<div id="content">This is an example, isn't it lovely.</div>}
104
100
  content.should =~ %r{<p class="byline">Written by Jon Wood.</p>}
105
101
  end
102
+
103
+ it "accepts field names as symbols" do
104
+ content = template.render(title: "Example Page", body: "This is an example.", author: "Jon Wood")
105
+
106
+ content.should =~ %r{<h1>Example Page</h1>}
107
+ end
106
108
  end
107
109
 
108
110
  context "when provided with fields that are not specified for the template" do
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+
5
+ describe Noumenon::Theme do
6
+ describe "loading from a directory" do
7
+ context "when the directory does not exist" do
8
+ it "raises a Theme::NotFoundError" do
9
+ lambda {
10
+ Noumenon::Theme.load File.join(File.dirname(__FILE__), "non_existant")
11
+ }.should raise_error Noumenon::Theme::NotFoundError
12
+ end
13
+ end
14
+
15
+ context "when no theme.yml file exists in the directory" do
16
+ it "raises a Theme::NotFoundError" do
17
+ with_temporary_theme(nil) do
18
+ lambda {
19
+ Noumenon::Theme.load theme_path
20
+ }.should raise_error Noumenon::Theme::NotFoundError
21
+ end
22
+ end
23
+ end
24
+
25
+ context "when a theme.yml file exists" do
26
+ let(:description) do
27
+ { "name" => "Example Theme",
28
+ "author" => "Jon Wood",
29
+ "email" => "jon@blankpad.net",
30
+ "copyright" => "Blank Pad Development, 2011",
31
+ "license" => "MIT" }
32
+ end
33
+
34
+ around(:each) do |example|
35
+ with_temporary_theme(description) do
36
+ example.run
37
+ end
38
+ end
39
+
40
+ it "doesn't raise any errors on load" do
41
+ lambda {
42
+ Noumenon::Theme.load theme_path
43
+ }.should_not raise_error
44
+ end
45
+
46
+ it "returns an instance of Noumenon::Theme" do
47
+ Noumenon::Theme.load(theme_path).should be_instance_of Noumenon::Theme
48
+ end
49
+
50
+ it "registers the theme for use" do
51
+ theme = Noumenon::Theme.load(theme_path)
52
+
53
+ Noumenon::Theme.themes.keys.should include "Example Theme"
54
+ Noumenon::Theme.themes["Example Theme"].should == theme
55
+ end
56
+
57
+ it "sets the path the theme has been loaded from" do
58
+ theme = Noumenon::Theme.load theme_path
59
+ theme.path.should == theme_path
60
+ end
61
+
62
+ %w(name author email copyright license).each do |field|
63
+ it "reads the #{field} from the theme description" do
64
+ theme = Noumenon::Theme.load theme_path
65
+ theme.send(field).should == description[field]
66
+ end
67
+ end
68
+
69
+ it "can load symbolised keys as well" do
70
+ theme = Noumenon::Theme.new(theme_path, name: "Example")
71
+ theme.name.should == "Example"
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "a loaded theme" do
77
+ around do |example|
78
+ with_temporary_theme(name: "Example Theme") do
79
+ example.run
80
+ end
81
+ end
82
+
83
+ let(:theme) { Noumenon::Theme.load theme_path }
84
+
85
+ describe "loading a template" do
86
+ it "when the template did not exist, it raises Template::NotFoundError" do
87
+ lambda { theme.template("not_found") }.should raise_error Noumenon::Template::NotFoundError
88
+ end
89
+
90
+ context "when the template did exist" do
91
+ let(:template) { "<h1>Hello, world!</h1>" }
92
+ subject do
93
+ File.open(File.join(theme_path, "templates", "example"), "w") { |f| f.print(template) }
94
+ theme.template("example")
95
+ end
96
+
97
+ it "returns an instance of Noumenon::Template" do
98
+ subject.should be_instance_of Noumenon::Template
99
+ end
100
+
101
+ it "loads the correct template" do
102
+ subject.content.should == "<h1>Hello, world!</h1>"
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "loading a layout" do
108
+ it "when the template did not exist, it raises Template::NotFoundError" do
109
+ lambda { theme.layout("not_found") }.should raise_error Noumenon::Template::NotFoundError
110
+ end
111
+
112
+ context "when the template did exist" do
113
+ let(:template) { "<h1>Hello, world!</h1>" }
114
+ subject do
115
+ File.open(File.join(theme_path, "layouts", "example"), "w") { |f| f.print(template) }
116
+ theme.layout("example")
117
+ end
118
+
119
+ it "returns an instance of Noumenon::Template" do
120
+ subject.should be_instance_of Noumenon::Template
121
+ end
122
+
123
+ it "loads the correct template" do
124
+ subject.content.should == "<h1>Hello, world!</h1>"
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -1,99 +1,43 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Noumenon do
4
- specify { Noumenon.should respond_to(:boot) }
5
-
6
- def app
7
- Noumenon.boot
8
- end
9
-
10
- describe "setting configuration" do
11
- before(:each) do
12
- Noumenon.config = nil
13
- end
14
-
15
- specify { Noumenon.should respond_to(:configure) }
16
- specify { Noumenon.should respond_to(:config) }
17
- specify { Noumenon.should respond_to(:config=) }
18
-
19
- it "sets config to the provided object on config=" do
20
- config = Noumenon::Configuration.new
21
- Noumenon.config = config
22
- Noumenon.config.should == config
4
+ describe "managing the content repository" do
5
+ it "can be set" do
6
+ Noumenon.content_repository = Noumenon::Repository.new
23
7
  end
24
8
 
25
- it "returns a new config instance if no config was previously set" do
26
- Noumenon.config.should be_instance_of Noumenon::Configuration
27
- end
28
-
29
- it "yields the config instance if a block is provided" do
30
- yielded = false
31
-
32
- config = Noumenon::Configuration.new
33
- Noumenon.config = config
34
- Noumenon.configure do |c|
35
- c.should == config
36
- yielded = true
37
- end
38
-
39
- yielded.should be_true
9
+ it "can be retrieved" do
10
+ repo = Noumenon::Repository.new
11
+ Noumenon.content_repository = repo
12
+ Noumenon.content_repository.should == repo
40
13
  end
41
14
  end
42
15
 
43
- describe "mounting apps in sub-directories" do
44
- context "a config.yml file is present in a sub-directory" do
45
- context "and the specified application exists" do
46
- it "uses that application to respond to requests at the specified directory" do
47
- get "/mounted_app"
48
-
49
- last_response.status.should == 200
50
- last_response.body.should == "This was served by Noumenon::Spec::ExampleApp"
51
- end
16
+ describe "setting the theme" do
17
+ context "when assigning with a Theme object" do
18
+ it "sets the theme directly" do
19
+ theme = Noumenon::Theme.new("/tmp")
20
+ Noumenon.theme = theme
21
+ Noumenon.theme.should == theme
52
22
  end
23
+ end
53
24
 
54
- context "and the specified application does not exist" do
55
- it "raises a MissingApplicationError" do
56
- Noumenon.config.content_repository_path = fixture_path("missing_application")
57
-
58
- raised = false
59
- begin
60
- app
61
- rescue Noumenon::MissingApplicationError => e
62
- raised = true
63
- e.to_s.should == "The application NonExistantApplication has not been loaded."
64
- end
65
-
66
- raised.should be_true
25
+ context "when assigning a theme name" do
26
+ around do |example|
27
+ with_temporary_theme do
28
+ example.run
67
29
  end
68
30
  end
69
- end
70
- end
71
-
72
- describe "themes" do
73
- before(:each) do
74
- Noumenon.instance_eval { @themes = nil }
75
- end
76
-
77
- specify { Noumenon.should respond_to(:register_theme) }
78
- specify { Noumenon.should respond_to(:themes) }
79
-
80
- it "defaults the theme list to an empty hash" do
81
- Noumenon.themes.should == {}
82
- end
83
-
84
- describe "registering a theme" do
85
- before(:each) { Noumenon.register_theme("external_theme", fixture_path("themes/external_theme")) }
86
31
 
87
- it "adds the registered theme to the list of available themes" do
88
- Noumenon.themes.should == {
89
- "external_theme" => { :path => fixture_path("themes/external_theme") }
90
- }
32
+ it "sets the theme if it has been registered" do
33
+ Noumenon::Theme.load(theme_path)
34
+
35
+ Noumenon.theme = "Example Theme"
36
+ Noumenon.theme.should == Noumenon::Theme.themes["Example Theme"]
91
37
  end
92
38
 
93
- it "should allow access to the theme's assets from /themes/theme_name/asset_name" do
94
- get "/themes/external_theme/example.txt"
39
+ it "raises an ArgumentError if it has not been registered" do
95
40
 
96
- last_response.body.should == fixture("themes/external_theme/assets/example.txt")
97
41
  end
98
42
  end
99
43
  end
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,12 @@
1
+ # Load anything in spec/support
2
+ Dir.glob(File.join(File.expand_path("..", __FILE__), "support", "**", "*.rb")).each { |f| require f }
3
+
1
4
  require 'noumenon'
2
- require 'noumenon/spec'
3
- require 'rack/test'
4
5
 
5
6
  RSpec.configure do |spec|
6
- spec.include Rack::Test::Methods
7
- spec.include Noumenon::Spec
8
- include Noumenon::Spec::Fixtures
9
-
10
- Noumenon::Core.set :environment, :test
7
+ include Noumenon::Spec::ThemeHelpers
11
8
 
12
9
  spec.before(:each) do
13
- Noumenon.register_theme "example", fixture_path("themes/example")
14
- Noumenon.register_theme "example_without_layout", fixture_path("themes/example_without_layout")
15
-
16
- Noumenon.configure do |c|
17
- c.content_repository_path = fixture_path("static_example")
18
- c.theme = "example"
19
- end
10
+ # Configure Noumenon
20
11
  end
21
12
  end
@@ -0,0 +1,45 @@
1
+ RSpec::Matchers.define :create_file do |path|
2
+ match do |block|
3
+ block.call
4
+ File.exist?(path)
5
+ end
6
+
7
+ failure_message do
8
+ "expected a file to be created at #{File.expand_path(path)}"
9
+ end
10
+
11
+ description do
12
+ "create a file at #{File.expand_path(path)}"
13
+ end
14
+ end
15
+
16
+ RSpec::Matchers.define :create_directory do |path|
17
+ match do |block|
18
+ block.call
19
+ File.exist?(path) && File.lstat(path).directory?
20
+ end
21
+
22
+ failure_message do
23
+ "expected a directory to be created at #{File.expand_path(path)}"
24
+ end
25
+
26
+ description do
27
+ "create a directory at #{File.expand_path(path)}"
28
+ end
29
+ end
30
+
31
+ RSpec::Matchers.define :move_file do |paths|
32
+ match do |block|
33
+ content = File.read(paths[:from])
34
+ block.call
35
+ File.exist?(paths[:to]) && !File.exist?(paths[:from]) && File.read(paths[:to]) == content
36
+ end
37
+
38
+ failure_message do
39
+ "expected the file at #{File.expand_path(paths[:from])} to move to #{File.expand_path(paths[:to])}"
40
+ end
41
+
42
+ description do
43
+ "move the file at #{File.expand_path(paths[:from])} to #{File.expand_path(paths[:to])}"
44
+ end
45
+ end
@@ -0,0 +1 @@
1
+ <h1>Hello!</h1>
@@ -19,5 +19,5 @@ author:
19
19
  <h1>{{ title }}</h1>
20
20
  <div id="content">{{ body }}</div>
21
21
  {% if author %}
22
- <p class="byline">Written by {{ author }}.</p>
22
+ <p class="byline">Written by {{ author }}.</p>
23
23
  {% endif %}