noumenon 0.1.3 → 0.1.4
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.
- data/CHANGELOG.md +15 -2
- data/README.md +15 -4
- data/Rakefile +5 -0
- data/features/asset_serving/from_content_repositories.feature +32 -0
- data/features/{theme_assets.feature → asset_serving/from_themes.feature} +1 -1
- data/features/step_definitions/asset_steps.rb +13 -0
- data/features/support/env.rb +6 -3
- data/features/template_tags/asset_paths.feature +30 -0
- data/features/template_tags/nav_menu.feature +19 -0
- data/lib/noumenon/core.rb +8 -1
- data/lib/noumenon/repository/file_system.rb +43 -6
- data/lib/noumenon/repository.rb +58 -4
- data/lib/noumenon/template/core_tags.rb +25 -2
- data/lib/noumenon/version.rb +1 -1
- data/lib/noumenon.rb +17 -1
- data/noumenon.gemspec +11 -11
- data/spec/noumenon/repository/file_system_spec.rb +37 -2
- data/spec/noumenon/repository_spec.rb +9 -43
- data/spec/noumenon_spec.rb +17 -1
- data/spec/support/file_matchers.rb +29 -7
- data/spec/support/implementation_matcher.rb +55 -0
- metadata +30 -24
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# 0.1.4
|
2
|
+
|
3
|
+
## New Features
|
4
|
+
|
5
|
+
* Support for saving assets to the content repository, and selecting an alternative repository
|
6
|
+
such as S3 for static assets.
|
7
|
+
* Added the `asset` and `theme_asset` template tags to generate URLs.
|
8
|
+
* Allow use of a `nav_title` attribute to put a different title in the navigation menu to the page itself.
|
9
|
+
|
10
|
+
## Bug Fixes
|
11
|
+
|
12
|
+
* Fix the template tags link from the documentation to the wiki.
|
13
|
+
* Make sure future releases of gems can't break Noumenon by restricting future versions
|
14
|
+
in the gemspec.
|
15
|
+
|
1
16
|
# 0.1.3
|
2
17
|
|
3
18
|
## New Features
|
@@ -6,8 +21,6 @@
|
|
6
21
|
* Make RSpec output color when running Rake
|
7
22
|
* Tidied up the Cucumber features
|
8
23
|
|
9
|
-
## Bug Fixes
|
10
|
-
|
11
24
|
# 0.1.2
|
12
25
|
|
13
26
|
## New Features
|
data/README.md
CHANGED
@@ -14,10 +14,18 @@ editors thats more likely to be a web interface.
|
|
14
14
|
|
15
15
|
## Getting Started
|
16
16
|
|
17
|
-
If you want to skip the theory, and
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
If you want to skip the theory, and get on with the fun bit, follow these steps:
|
18
|
+
|
19
|
+
```shell
|
20
|
+
$ gem install noumenon thin bundler
|
21
|
+
$ noumenon my_site
|
22
|
+
$ cd my_site
|
23
|
+
$ bundle install
|
24
|
+
$ thin start
|
25
|
+
```
|
26
|
+
|
27
|
+
You're basic Noumenon site will be running at http://localhost:3000/ now. If you want to put it on the internet it should
|
28
|
+
work straight out of the box on Heroku.
|
21
29
|
|
22
30
|
## How it Works
|
23
31
|
|
@@ -122,6 +130,9 @@ Currently the only available content repository uses YAML files to store your co
|
|
122
130
|
and the API for content repositories is simple enough that just about any datastore could be used if you want to implement an
|
123
131
|
adapater for it.
|
124
132
|
|
133
|
+
Content repositories can also store static assets such as images for use within your pages. For the default file system repository
|
134
|
+
just put them below the `assets` directory, which is made available at the URL `/assets/` by Noumenon.
|
135
|
+
|
125
136
|
### Applications
|
126
137
|
|
127
138
|
If part of your site requires something other then rendering a piece of content within a static template then you can use a
|
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'bundler/gem_helper'
|
1
2
|
require 'rspec/core/rake_task'
|
2
3
|
require 'cucumber/rake/task'
|
3
4
|
require 'yard'
|
@@ -20,3 +21,7 @@ end
|
|
20
21
|
YARD::Rake::YardocTask.new do |t|
|
21
22
|
t.name = :doc
|
22
23
|
end
|
24
|
+
|
25
|
+
namespace :gem do
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: Serving assets from the content repository
|
2
|
+
As a user
|
3
|
+
I want to have assets in my content repository
|
4
|
+
So that I can attach my own images to pages
|
5
|
+
|
6
|
+
Scenario: The asset has been added
|
7
|
+
Given I have uploaded this asset as "example.txt" to the asset repository
|
8
|
+
"""
|
9
|
+
Hello, world!
|
10
|
+
"""
|
11
|
+
When I view "/assets/example.txt"
|
12
|
+
Then the page content should be:
|
13
|
+
"""
|
14
|
+
Hello, world!
|
15
|
+
"""
|
16
|
+
|
17
|
+
Scenario: The asset does not exist
|
18
|
+
Given there is no asset called "example.txt" in the asset repository
|
19
|
+
When I view "/assets/example.txt"
|
20
|
+
Then the page should return a 404 error
|
21
|
+
|
22
|
+
Scenario: Using an alternative asset repository
|
23
|
+
Given I have chosen to use a seperate asset repository
|
24
|
+
And I have uploaded this asset as "example.txt" to the asset repository
|
25
|
+
"""
|
26
|
+
Alternative repos FTW
|
27
|
+
"""
|
28
|
+
When I view "/assets/example.txt"
|
29
|
+
Then the page content should be:
|
30
|
+
"""
|
31
|
+
Alternative repos FTW
|
32
|
+
"""
|
@@ -5,3 +5,16 @@ end
|
|
5
5
|
Given /^the asset "([^"]*)" exists within the theme with this content:$/ do |asset_name, content|
|
6
6
|
File.open(File.join(Noumenon.theme.path, "assets", asset_name), "w") { |f| f.print content }
|
7
7
|
end
|
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::Repository::FileSystem.new(path: File.join(tmp_path, "assets"))
|
12
|
+
end
|
13
|
+
|
14
|
+
Given /^I have uploaded this asset as "([^"]*)" to the asset repository$/ do |path, string|
|
15
|
+
Noumenon.asset_repository.save_asset(path, string)
|
16
|
+
end
|
17
|
+
|
18
|
+
Given /^there is no asset called "([^"]*)" in the asset repository$/ do |path|
|
19
|
+
# noop
|
20
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -17,9 +17,13 @@ Capybara.app = Noumenon.server
|
|
17
17
|
World do
|
18
18
|
include RSpec::Expectations
|
19
19
|
include RSpec::Matchers
|
20
|
+
|
21
|
+
def tmp_path
|
22
|
+
File.expand_path('../tmp', __FILE__)
|
23
|
+
end
|
20
24
|
|
21
25
|
def content_path
|
22
|
-
File.
|
26
|
+
File.join(tmp_path, "content")
|
23
27
|
end
|
24
28
|
|
25
29
|
true
|
@@ -33,6 +37,5 @@ Before do
|
|
33
37
|
end
|
34
38
|
|
35
39
|
After do
|
36
|
-
FileUtils.rm_r
|
37
|
-
FileUtils.rm_r theme_path
|
40
|
+
FileUtils.rm_r tmp_path
|
38
41
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Feature: Generating asset paths from templates
|
2
|
+
As a designer
|
3
|
+
I want to use a tag to reference asset paths in templates
|
4
|
+
So that they keep working once deployed
|
5
|
+
|
6
|
+
Scenario: Generating a path for a theme asset
|
7
|
+
Given this template exists at "theme_assets.nou.html"
|
8
|
+
"""
|
9
|
+
<img src="{% theme_asset logo.png %}" />
|
10
|
+
"""
|
11
|
+
And this content item exists at "/theme_assets"
|
12
|
+
| template | theme_assets |
|
13
|
+
When I view "/theme_assets"
|
14
|
+
Then the page content should be:
|
15
|
+
"""
|
16
|
+
<img src="/themes/Example%20Theme/logo.png" />
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: Generating a URL for a content asset
|
20
|
+
Given this template exists at "content_assets.nou.html"
|
21
|
+
"""
|
22
|
+
<img src="{% asset attachment.png %}" />
|
23
|
+
"""
|
24
|
+
And this content item exists at "/content_assets"
|
25
|
+
| template | content_assets |
|
26
|
+
When I view "/content_assets"
|
27
|
+
Then the page content should be:
|
28
|
+
"""
|
29
|
+
<img src="/assets/attachment.png" />
|
30
|
+
"""
|
@@ -23,6 +23,25 @@ Feature: Navigation menus
|
|
23
23
|
| About | /about |
|
24
24
|
| Contact | /contact |
|
25
25
|
| Team | /team |
|
26
|
+
|
27
|
+
Scenario: Custom page titles
|
28
|
+
Given this template exists at "basic_menu.nou.html"
|
29
|
+
"""
|
30
|
+
<nav>
|
31
|
+
{% nav_menu %}
|
32
|
+
</nav>
|
33
|
+
"""
|
34
|
+
And this content item exists at "/about"
|
35
|
+
| title | About |
|
36
|
+
| template | basic_menu |
|
37
|
+
And this content item exists at "/contact"
|
38
|
+
| title | Get In Touch With Us |
|
39
|
+
| nav_title | Contact |
|
40
|
+
When I view "/about"
|
41
|
+
Then a menu with the following items should be visible:
|
42
|
+
| Label | Link Target |
|
43
|
+
| About | /about |
|
44
|
+
| Contact | /contact |
|
26
45
|
|
27
46
|
Scenario: Multi-level menu
|
28
47
|
Given this template exists at "deep_menu.nou.html"
|
data/lib/noumenon/core.rb
CHANGED
@@ -40,7 +40,14 @@ class Noumenon::Core < Sinatra::Base
|
|
40
40
|
def content
|
41
41
|
Noumenon.content_repository
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
|
+
get "/assets/*" do |path|
|
45
|
+
asset = Noumenon.asset_repository.get_asset("/#{path}")
|
46
|
+
halt 404, "Asset not found" unless asset
|
47
|
+
|
48
|
+
asset
|
49
|
+
end
|
50
|
+
|
44
51
|
get "*" do |path|
|
45
52
|
page = content.get(path)
|
46
53
|
|
@@ -38,10 +38,8 @@ class Noumenon::Repository::FileSystem < Noumenon::Repository
|
|
38
38
|
# @return [ Hash, #each, nil ] The piece of content, or nil if it could not be found.
|
39
39
|
# @api public
|
40
40
|
def get(path, check_for_index = true)
|
41
|
-
|
42
|
-
|
43
|
-
if File.exist?(file)
|
44
|
-
YAML.load(File.read(file)).symbolize_keys
|
41
|
+
if content = read_file("#{path}.yml")
|
42
|
+
YAML.load(content).symbolize_keys
|
45
43
|
elsif check_for_index
|
46
44
|
return get("#{path}/index", false)
|
47
45
|
end
|
@@ -50,13 +48,18 @@ class Noumenon::Repository::FileSystem < Noumenon::Repository
|
|
50
48
|
# Saves a piece of content to the repsitory.
|
51
49
|
#
|
52
50
|
# @see Noumenon::Repository#put
|
51
|
+
# @api public
|
53
52
|
def put(path, content = {})
|
54
53
|
create_tree(path)
|
55
|
-
|
54
|
+
|
56
55
|
path = File.join(path, "index") if File.exist?(repository_path(path))
|
57
|
-
|
56
|
+
write_file "#{path}.yml", content.symbolize_keys.to_yaml
|
58
57
|
end
|
59
58
|
|
59
|
+
# Return any content items below the path specified.
|
60
|
+
#
|
61
|
+
# @see Noumenon::Repository#children
|
62
|
+
# @api public
|
60
63
|
def children(root = "/", depth = 1)
|
61
64
|
root.gsub! /\/$/, ''
|
62
65
|
|
@@ -79,6 +82,24 @@ class Noumenon::Repository::FileSystem < Noumenon::Repository
|
|
79
82
|
page
|
80
83
|
end
|
81
84
|
end
|
85
|
+
|
86
|
+
# Save a static asset to the repository.
|
87
|
+
#
|
88
|
+
# @see Noumenon::Repository#save_asset
|
89
|
+
# @api public
|
90
|
+
def save_asset(path, content)
|
91
|
+
raise ArgumentError.new("Assets must have a file extension.") unless File.extname(path).size > 0
|
92
|
+
write_file File.join("assets", path), content
|
93
|
+
end
|
94
|
+
|
95
|
+
# Retreive a static asset to the repository.
|
96
|
+
#
|
97
|
+
# @see Noumenon::Repository#get_asset
|
98
|
+
# @api public
|
99
|
+
def get_asset(path)
|
100
|
+
return nil unless File.extname(path).size > 0
|
101
|
+
read_file File.join("assets", path)
|
102
|
+
end
|
82
103
|
|
83
104
|
private
|
84
105
|
# Return the on-disk path to a repository item.
|
@@ -97,6 +118,22 @@ class Noumenon::Repository::FileSystem < Noumenon::Repository
|
|
97
118
|
end
|
98
119
|
end
|
99
120
|
|
121
|
+
def write_file(path, content)
|
122
|
+
path_on_disk = repository_path(path)
|
123
|
+
directory = File.dirname(path_on_disk)
|
124
|
+
FileUtils.mkdir_p(directory) unless File.exist?(directory)
|
125
|
+
|
126
|
+
File.open(path_on_disk, "w") { |f| f.print content }
|
127
|
+
end
|
128
|
+
|
129
|
+
def read_file(path)
|
130
|
+
begin
|
131
|
+
File.read(repository_path(path))
|
132
|
+
rescue Errno::ENOENT => e
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
100
137
|
# Moves conflicting YAML files into directory/index.yml
|
101
138
|
#
|
102
139
|
# For example given
|
data/lib/noumenon/repository.rb
CHANGED
@@ -25,7 +25,7 @@ class Noumenon::Repository
|
|
25
25
|
# @param [ Hash, #each ] content A hash of key/value pairs to use as the content.
|
26
26
|
# @api public
|
27
27
|
def put(path, content)
|
28
|
-
|
28
|
+
not_supported "updating it's contents"
|
29
29
|
end
|
30
30
|
|
31
31
|
# Load an item from the repository. If the item does not exist then `nil` should be returned.
|
@@ -34,10 +34,64 @@ class Noumenon::Repository
|
|
34
34
|
# @return [ Hash, #each, nil ] Either the hash stored at the specified path, or nil if no content was found.
|
35
35
|
# @api public
|
36
36
|
def get(path)
|
37
|
-
|
37
|
+
not_supported "reading it's contents"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an array of content items below the root specified. If depth is greater then 1 that
|
41
|
+
# many sub-directories will also be checked, with any items in those being placed in a `:children` attribute
|
42
|
+
# on the parent.
|
43
|
+
#
|
44
|
+
# @param [ #to_s ] root The path to start searching from.
|
45
|
+
# @param [ Integer ] depth The number of sub-directories to check.
|
46
|
+
# @return [ Array, #each ] An array of child items.
|
47
|
+
# @api public
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# Noumenon.content_repository.children("/", 2)
|
51
|
+
# # => [
|
52
|
+
# # {
|
53
|
+
# # path: "/about", title: "About, children: [
|
54
|
+
# # { path: "/about/team", title: "The Team" },
|
55
|
+
# # { path: "/about/area", title: "The Area" }
|
56
|
+
# # ]
|
57
|
+
# # }
|
58
|
+
# # ]
|
59
|
+
#
|
60
|
+
def children(root = "/", depth = 1)
|
61
|
+
not_supported "listing children from a path"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Saves a static asset to the repository. To be saved the asset must have a file extension, to prevent
|
65
|
+
# problems with an entire directory being overridden by a single asset.
|
66
|
+
#
|
67
|
+
# @param [ String ] path The path to save the asset at.
|
68
|
+
# @param [ String ] content The contents of the asset.
|
69
|
+
# @raises [ ArgumentError ] The path did not have a file extension.
|
70
|
+
def save_asset(path, content)
|
71
|
+
not_supported "saving assets"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Retrieves a static asset from the repository. If the asset does not exist nil is returned.
|
75
|
+
#
|
76
|
+
# @param [ String ] path The path to load from.
|
77
|
+
# @return [ String, nil ] The asset's content, or nil if it does not exist.
|
78
|
+
def get_asset(path)
|
79
|
+
not_supported "loading assets"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a URL for the specified asset, without checking for it's existence.
|
83
|
+
#
|
84
|
+
# This is most useful for repositories that save their assets to a CDN such as S3, and allows them to be
|
85
|
+
# directly accessed from there, instead of downloaded and then retransmitted.
|
86
|
+
#
|
87
|
+
# @param [ String ] path The asset path within the repository.
|
88
|
+
# @return [ String ] The URL to use when requesting the asset.
|
89
|
+
def asset_url(path)
|
90
|
+
File.join("/assets", path)
|
38
91
|
end
|
39
92
|
|
40
|
-
|
41
|
-
|
93
|
+
protected
|
94
|
+
def not_supported(action)
|
95
|
+
raise NotImplementedError.new("This repository type does not support #{action}.")
|
42
96
|
end
|
43
97
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'noumenon/template'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
# Liquid extensions provided by Noumenon.
|
4
5
|
#
|
5
|
-
# @see http://github.com/Noumenon/noumenon/wiki/Template
|
6
|
+
# @see http://github.com/Noumenon/noumenon/wiki/Template-Tags
|
6
7
|
module Noumenon::Template::CoreTags
|
7
8
|
# The core set of filters provided by Noumenon for use in templates.
|
8
9
|
#
|
@@ -74,7 +75,7 @@ module Noumenon::Template::CoreTags
|
|
74
75
|
def render_list(items)
|
75
76
|
list = items.collect do |item|
|
76
77
|
str = "<li>"
|
77
|
-
str << %Q{<a href="#{item[:path]}">#{item[:title]}</a>}
|
78
|
+
str << %Q{<a href="#{item[:path]}">#{item[:nav_title] || item[:title]}</a>}
|
78
79
|
str << render_list(item[:children]) if item[:children]
|
79
80
|
str << "</li>"
|
80
81
|
|
@@ -89,4 +90,26 @@ module Noumenon::Template::CoreTags
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
Liquid::Template.register_tag('nav_menu', NavigationTag)
|
93
|
+
|
94
|
+
class ThemeAssetTag < Liquid::Tag
|
95
|
+
def initialize(tag_name, asset, tokens)
|
96
|
+
@asset = asset.strip
|
97
|
+
end
|
98
|
+
|
99
|
+
def render(context)
|
100
|
+
File.join("/themes", URI.escape(Noumenon.theme.name), @asset)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
Liquid::Template.register_tag('theme_asset', ThemeAssetTag)
|
104
|
+
|
105
|
+
class ContentAssetTag < Liquid::Tag
|
106
|
+
def initialize(tag_name, asset, tokens)
|
107
|
+
@asset = asset.strip
|
108
|
+
end
|
109
|
+
|
110
|
+
def render(context)
|
111
|
+
Noumenon.asset_repository.asset_url(@asset)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
Liquid::Template.register_tag('asset', ContentAssetTag)
|
92
115
|
end
|
data/lib/noumenon/version.rb
CHANGED
data/lib/noumenon.rb
CHANGED
@@ -47,7 +47,23 @@ module Noumenon
|
|
47
47
|
def self.content_repository=(repository)
|
48
48
|
@content_repository = repository
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
|
+
# Returns the current asset repository. If one hasn't been set then the content repository will be returned.
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
# @return [ Noumenon::Repository, #get_asset, #save_asset ]
|
55
|
+
def self.asset_repository
|
56
|
+
@asset_repository || content_repository
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sets the current asset repository. If you want to return to using the default content repository set to nil.
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
# @param [ Noumenon::Repository, #get_asset, #save_asset ] repository The repository to use for assets.
|
63
|
+
def self.asset_repository=(repository)
|
64
|
+
@asset_repository = repository
|
65
|
+
end
|
66
|
+
|
51
67
|
# Returns the current theme
|
52
68
|
#
|
53
69
|
# @api public
|
data/noumenon.gemspec
CHANGED
@@ -31,16 +31,16 @@ EOF
|
|
31
31
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
32
32
|
s.require_paths = ["lib"]
|
33
33
|
|
34
|
-
s.add_dependency "facets", "
|
35
|
-
s.add_dependency "liquid", "
|
36
|
-
s.add_dependency "sinatra", "
|
34
|
+
s.add_dependency "facets", "~> 2.9"
|
35
|
+
s.add_dependency "liquid", "~> 2.2"
|
36
|
+
s.add_dependency "sinatra", "~> 1.2"
|
37
37
|
|
38
|
-
s.add_development_dependency "aruba", "
|
39
|
-
s.add_development_dependency "capybara", "
|
40
|
-
s.add_development_dependency "cucumber", "
|
41
|
-
s.add_development_dependency "rake", "
|
42
|
-
s.add_development_dependency "rdiscount", "
|
43
|
-
s.add_development_dependency "rspec", "
|
44
|
-
s.add_development_dependency "thor", "
|
45
|
-
s.add_development_dependency "yard", "
|
38
|
+
s.add_development_dependency "aruba", "~> 0.3"
|
39
|
+
s.add_development_dependency "capybara", "~> 1.0.0.beta1"
|
40
|
+
s.add_development_dependency "cucumber", "~> 0.10"
|
41
|
+
s.add_development_dependency "rake", "~> 0.9"
|
42
|
+
s.add_development_dependency "rdiscount", "~> 1.6"
|
43
|
+
s.add_development_dependency "rspec", "~> 2.5"
|
44
|
+
s.add_development_dependency "thor", "~> 0.14"
|
45
|
+
s.add_development_dependency "yard", "~> 0.7"
|
46
46
|
end
|
@@ -29,7 +29,7 @@ describe Noumenon::Repository::FileSystem do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
it { should
|
32
|
+
it { should implement(:put, "/", foo: "bar") }
|
33
33
|
|
34
34
|
describe "putting content in the repository" do
|
35
35
|
context "when writing to a top level file" do
|
@@ -80,7 +80,8 @@ describe Noumenon::Repository::FileSystem do
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
|
+
it { should implement(:get, "/") }
|
84
85
|
describe "retrieving content from the repository" do
|
85
86
|
context "when the item does not exist" do
|
86
87
|
it "returns nil" do
|
@@ -113,6 +114,8 @@ describe Noumenon::Repository::FileSystem do
|
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
117
|
+
it { should implement(:children) }
|
118
|
+
|
116
119
|
describe "listing child items" do
|
117
120
|
before do
|
118
121
|
subject.put("/", title: "Home")
|
@@ -170,4 +173,36 @@ describe Noumenon::Repository::FileSystem do
|
|
170
173
|
end
|
171
174
|
end
|
172
175
|
end
|
176
|
+
|
177
|
+
it { should implement(:save_asset, "/foo.txt", "foo") }
|
178
|
+
describe "saving assets" do
|
179
|
+
it "raises an ArgumentError when the path has no extension" do
|
180
|
+
lambda { subject.save_asset("/foo", "Content") }.should raise_error(ArgumentError, "Assets must have a file extension.")
|
181
|
+
end
|
182
|
+
|
183
|
+
it "saves the asset to the filesystem" do
|
184
|
+
lambda { subject.save_asset("/foo.txt", "Content") }.should create_file(File.join(content_path, "assets", "foo.txt")).with_content("Content")
|
185
|
+
end
|
186
|
+
|
187
|
+
it "creates any neccesary parent directories" do
|
188
|
+
lambda { subject.save_asset("/bar/foo.txt", "Content") }.should create_file(File.join(content_path, "assets", "bar", "foo.txt"))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
it { should implement(:get_asset, "/foo.txt") }
|
193
|
+
describe "retrieving assets" do
|
194
|
+
it "returns nil if the path provided is not a file with an extension" do
|
195
|
+
subject.get_asset("/foo").should be_nil
|
196
|
+
subject.get_asset("/foo/").should be_nil
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns nil if the asset did not exist" do
|
200
|
+
subject.get_asset("/foo.txt").should be_nil
|
201
|
+
end
|
202
|
+
|
203
|
+
it "returns the file's contents if ithe asset does exist" do
|
204
|
+
subject.save_asset("/foo.txt", "Content")
|
205
|
+
subject.get_asset("/foo.txt").should == "Content"
|
206
|
+
end
|
207
|
+
end
|
173
208
|
end
|
@@ -7,49 +7,15 @@ describe Noumenon::Repository do
|
|
7
7
|
repo.options[:foo].should == "bar"
|
8
8
|
end
|
9
9
|
end
|
10
|
+
|
11
|
+
it { should_not implement(:put, "/", foo: "bar").with_message("This repository type does not support updating it's contents.") }
|
12
|
+
it { should_not implement(:get, "/").with_message("This repository type does not support reading it's contents.") }
|
13
|
+
it { should_not implement(:children).with_message("This repository type does not support listing children from a path.") }
|
14
|
+
it { should_not implement(:save_asset, "/example.txt", "Content").with_message("This repository type does not support saving assets.") }
|
15
|
+
it { should_not implement(:get_asset, "/example.txt").with_message("This repository type does not support loading assets.") }
|
10
16
|
|
11
|
-
it { should
|
12
|
-
|
13
|
-
|
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
|
-
|
41
|
-
it { should respond_to(:children) }
|
42
|
-
describe "calling #children" do
|
43
|
-
it "raises a NotImplementedError" do
|
44
|
-
lambda { subject.children("/") }.should raise_error NotImplementedError
|
45
|
-
end
|
46
|
-
|
47
|
-
it "provides some details in the error message" do
|
48
|
-
begin
|
49
|
-
subject.children("/")
|
50
|
-
rescue NotImplementedError => e
|
51
|
-
e.to_s.should == "This repository type does not support listing children from a path."
|
52
|
-
end
|
53
|
-
end
|
17
|
+
it { should implement(:asset_url, "/example.txt") }
|
18
|
+
it "returns the default URL below /assets/ when requested" do
|
19
|
+
subject.asset_url("/example.txt").should == "/assets/example.txt"
|
54
20
|
end
|
55
21
|
end
|
data/spec/noumenon_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Noumenon do
|
4
|
-
describe "
|
4
|
+
describe "accessing the content repository" do
|
5
5
|
it "can be set" do
|
6
6
|
Noumenon.content_repository = Noumenon::Repository.new
|
7
7
|
end
|
@@ -12,6 +12,22 @@ describe Noumenon do
|
|
12
12
|
Noumenon.content_repository.should == repo
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
describe "accessing the asset repository" do
|
17
|
+
it "returns the content repository if it has not been set" do
|
18
|
+
Noumenon.content_repository = Noumenon::Repository.new
|
19
|
+
Noumenon.asset_repository.should === Noumenon.content_repository
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns the set asset repository if one has been set" do
|
23
|
+
content = Noumenon::Repository.new
|
24
|
+
assets = Noumenon::Repository.new
|
25
|
+
|
26
|
+
Noumenon.content_repository = content
|
27
|
+
Noumenon.asset_repository = assets
|
28
|
+
Noumenon.asset_repository.should === assets
|
29
|
+
end
|
30
|
+
end
|
15
31
|
|
16
32
|
describe "setting the theme" do
|
17
33
|
context "when assigning with a Theme object" do
|
@@ -1,18 +1,40 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
class CreateFileExpectation
|
2
|
+
def initialize(path)
|
3
|
+
@path = path
|
4
|
+
end
|
5
|
+
|
6
|
+
def with_content(content)
|
7
|
+
@content = content
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(block)
|
3
12
|
block.call
|
4
|
-
|
13
|
+
|
14
|
+
exists = File.exist?(@path)
|
15
|
+
if @content
|
16
|
+
exists && (File.read(@path) == @content)
|
17
|
+
else
|
18
|
+
exists
|
19
|
+
end
|
5
20
|
end
|
6
21
|
|
7
|
-
failure_message
|
8
|
-
"expected a file to be created at #{File.expand_path(path)}"
|
22
|
+
def failure_message
|
23
|
+
msg = "expected a file to be created at #{File.expand_path(@path)}"
|
24
|
+
msg << " with content #{@content}" if @content
|
25
|
+
|
26
|
+
msg
|
9
27
|
end
|
10
28
|
|
11
|
-
description
|
12
|
-
"create a file at #{File.expand_path(path)}"
|
29
|
+
def description
|
30
|
+
"create a file at #{File.expand_path(@path)}"
|
13
31
|
end
|
14
32
|
end
|
15
33
|
|
34
|
+
def create_file(block)
|
35
|
+
CreateFileExpectation.new(block)
|
36
|
+
end
|
37
|
+
|
16
38
|
RSpec::Matchers.define :create_directory do |path|
|
17
39
|
match do |block|
|
18
40
|
block.call
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class ImplementedExpectation
|
2
|
+
def initialize(method, *args)
|
3
|
+
@method = method
|
4
|
+
@args = args
|
5
|
+
@message = "NotImplementedError"
|
6
|
+
end
|
7
|
+
|
8
|
+
def with_message(message)
|
9
|
+
@message = message
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def matches?(obj)
|
14
|
+
implemented = true
|
15
|
+
|
16
|
+
begin
|
17
|
+
obj.send(@method, *@args)
|
18
|
+
rescue NotImplementedError => e
|
19
|
+
implemented = false
|
20
|
+
actual_message = e.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
unless negative_expectation?
|
24
|
+
implemented
|
25
|
+
else
|
26
|
+
implemented || actual_message != @message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message_for_should
|
31
|
+
"expected to implement #{@method}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def failure_message_for_should_not
|
35
|
+
message = "expected not to implement #{@method}"
|
36
|
+
if @message
|
37
|
+
message << " with the message '#{@message}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
message
|
41
|
+
end
|
42
|
+
|
43
|
+
def description
|
44
|
+
"implement #{@method}"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def negative_expectation?
|
49
|
+
caller.first(3).find { |s| s =~ /should_not/ }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def implement(method, *args)
|
54
|
+
ImplementedExpectation.new(method, *args)
|
55
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: noumenon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.4
|
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-05-
|
13
|
+
date: 2011-05-27 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -19,9 +19,9 @@ dependencies:
|
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
|
-
- -
|
22
|
+
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: 2.9
|
24
|
+
version: "2.9"
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ~>
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: "2.2"
|
36
36
|
type: :runtime
|
@@ -41,9 +41,9 @@ dependencies:
|
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
43
43
|
requirements:
|
44
|
-
- -
|
44
|
+
- - ~>
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.2
|
46
|
+
version: "1.2"
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id003
|
49
49
|
- !ruby/object:Gem::Dependency
|
@@ -52,9 +52,9 @@ dependencies:
|
|
52
52
|
requirement: &id004 !ruby/object:Gem::Requirement
|
53
53
|
none: false
|
54
54
|
requirements:
|
55
|
-
- -
|
55
|
+
- - ~>
|
56
56
|
- !ruby/object:Gem::Version
|
57
|
-
version: 0.3
|
57
|
+
version: "0.3"
|
58
58
|
type: :development
|
59
59
|
version_requirements: *id004
|
60
60
|
- !ruby/object:Gem::Dependency
|
@@ -63,7 +63,7 @@ dependencies:
|
|
63
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
64
64
|
none: false
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.0.0.beta1
|
69
69
|
type: :development
|
@@ -74,9 +74,9 @@ dependencies:
|
|
74
74
|
requirement: &id006 !ruby/object:Gem::Requirement
|
75
75
|
none: false
|
76
76
|
requirements:
|
77
|
-
- -
|
77
|
+
- - ~>
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: 0.10
|
79
|
+
version: "0.10"
|
80
80
|
type: :development
|
81
81
|
version_requirements: *id006
|
82
82
|
- !ruby/object:Gem::Dependency
|
@@ -85,9 +85,9 @@ dependencies:
|
|
85
85
|
requirement: &id007 !ruby/object:Gem::Requirement
|
86
86
|
none: false
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - ~>
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: 0.9
|
90
|
+
version: "0.9"
|
91
91
|
type: :development
|
92
92
|
version_requirements: *id007
|
93
93
|
- !ruby/object:Gem::Dependency
|
@@ -96,9 +96,9 @@ dependencies:
|
|
96
96
|
requirement: &id008 !ruby/object:Gem::Requirement
|
97
97
|
none: false
|
98
98
|
requirements:
|
99
|
-
- -
|
99
|
+
- - ~>
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 1.6
|
101
|
+
version: "1.6"
|
102
102
|
type: :development
|
103
103
|
version_requirements: *id008
|
104
104
|
- !ruby/object:Gem::Dependency
|
@@ -107,9 +107,9 @@ dependencies:
|
|
107
107
|
requirement: &id009 !ruby/object:Gem::Requirement
|
108
108
|
none: false
|
109
109
|
requirements:
|
110
|
-
- -
|
110
|
+
- - ~>
|
111
111
|
- !ruby/object:Gem::Version
|
112
|
-
version: 2.5
|
112
|
+
version: "2.5"
|
113
113
|
type: :development
|
114
114
|
version_requirements: *id009
|
115
115
|
- !ruby/object:Gem::Dependency
|
@@ -118,9 +118,9 @@ dependencies:
|
|
118
118
|
requirement: &id010 !ruby/object:Gem::Requirement
|
119
119
|
none: false
|
120
120
|
requirements:
|
121
|
-
- -
|
121
|
+
- - ~>
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 0.14
|
123
|
+
version: "0.14"
|
124
124
|
type: :development
|
125
125
|
version_requirements: *id010
|
126
126
|
- !ruby/object:Gem::Dependency
|
@@ -129,9 +129,9 @@ dependencies:
|
|
129
129
|
requirement: &id011 !ruby/object:Gem::Requirement
|
130
130
|
none: false
|
131
131
|
requirements:
|
132
|
-
- -
|
132
|
+
- - ~>
|
133
133
|
- !ruby/object:Gem::Version
|
134
|
-
version: 0.7
|
134
|
+
version: "0.7"
|
135
135
|
type: :development
|
136
136
|
version_requirements: *id011
|
137
137
|
description: |
|
@@ -165,6 +165,8 @@ files:
|
|
165
165
|
- README.md
|
166
166
|
- Rakefile
|
167
167
|
- bin/noumenon
|
168
|
+
- features/asset_serving/from_content_repositories.feature
|
169
|
+
- features/asset_serving/from_themes.feature
|
168
170
|
- features/generator/site_generator.feature
|
169
171
|
- features/mounted_applications.feature
|
170
172
|
- features/step_definitions/asset_steps.rb
|
@@ -179,10 +181,10 @@ files:
|
|
179
181
|
- features/template_extensions.feature
|
180
182
|
- features/template_rendering/dynamic.feature
|
181
183
|
- features/template_rendering/static.feature
|
184
|
+
- features/template_tags/asset_paths.feature
|
182
185
|
- features/template_tags/assign_to.feature
|
183
186
|
- features/template_tags/content_from.feature
|
184
187
|
- features/template_tags/nav_menu.feature
|
185
|
-
- features/theme_assets.feature
|
186
188
|
- generators/repository/index.yml
|
187
189
|
- generators/site/Gemfile
|
188
190
|
- generators/site/config.ru
|
@@ -213,6 +215,7 @@ files:
|
|
213
215
|
- spec/noumenon_spec.rb
|
214
216
|
- spec/spec_helper.rb
|
215
217
|
- spec/support/file_matchers.rb
|
218
|
+
- spec/support/implementation_matcher.rb
|
216
219
|
- spec/support/templates/basic_template.html
|
217
220
|
- spec/support/templates/template_with_fields.html
|
218
221
|
has_rdoc: true
|
@@ -244,6 +247,8 @@ signing_key:
|
|
244
247
|
specification_version: 3
|
245
248
|
summary: A flexible content management system.
|
246
249
|
test_files:
|
250
|
+
- features/asset_serving/from_content_repositories.feature
|
251
|
+
- features/asset_serving/from_themes.feature
|
247
252
|
- features/generator/site_generator.feature
|
248
253
|
- features/mounted_applications.feature
|
249
254
|
- features/step_definitions/asset_steps.rb
|
@@ -258,10 +263,10 @@ test_files:
|
|
258
263
|
- features/template_extensions.feature
|
259
264
|
- features/template_rendering/dynamic.feature
|
260
265
|
- features/template_rendering/static.feature
|
266
|
+
- features/template_tags/asset_paths.feature
|
261
267
|
- features/template_tags/assign_to.feature
|
262
268
|
- features/template_tags/content_from.feature
|
263
269
|
- features/template_tags/nav_menu.feature
|
264
|
-
- features/theme_assets.feature
|
265
270
|
- spec/noumenon/repository/file_system_spec.rb
|
266
271
|
- spec/noumenon/repository_spec.rb
|
267
272
|
- spec/noumenon/template_spec.rb
|
@@ -269,5 +274,6 @@ test_files:
|
|
269
274
|
- spec/noumenon_spec.rb
|
270
275
|
- spec/spec_helper.rb
|
271
276
|
- spec/support/file_matchers.rb
|
277
|
+
- spec/support/implementation_matcher.rb
|
272
278
|
- spec/support/templates/basic_template.html
|
273
279
|
- spec/support/templates/template_with_fields.html
|