noumenon 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 %}