noumenon 0.1.2 → 0.1.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.
- data/CHANGELOG.md +10 -0
- data/Rakefile +3 -1
- data/features/step_definitions/navigation_steps.rb +26 -0
- data/features/template_tags/assign_to.feature +15 -0
- data/features/template_tags/content_from.feature +33 -0
- data/features/template_tags/nav_menu.feature +77 -0
- data/lib/noumenon/repository/file_system.rb +23 -0
- data/lib/noumenon/repository.rb +4 -0
- data/lib/noumenon/template/core_tags.rb +51 -1
- data/lib/noumenon/version.rb +1 -1
- data/spec/noumenon/repository/file_system_spec.rb +58 -0
- data/spec/noumenon/repository_spec.rb +15 -0
- metadata +13 -7
- data/features/including_content_from_a_template.feature +0 -17
- /data/features/{dynamic_template_rendering.feature → template_rendering/dynamic.feature} +0 -0
- /data/features/{static_template_rendering.feature → template_rendering/static.feature} +0 -0
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
def check_for_items(menu_items)
|
2
|
+
menu_items.hashes.each do |item|
|
3
|
+
page.should have_css "li a[href='#{item["Link Target"]}']", :text => item["Label"]
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
Then /^a menu with the following items should be visible:$/ do |menu_items|
|
8
|
+
page.should have_css "nav ul"
|
9
|
+
within "nav ul" do
|
10
|
+
check_for_items(menu_items)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^the "([^"]*)" menu item should have these children:$/ do |label, items|
|
15
|
+
within("nav ul li", :text => label) do
|
16
|
+
check_for_items(items)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^these menu items should not be visible:$/ do |items|
|
21
|
+
within "nav ul" do
|
22
|
+
items.hashes.each do |item|
|
23
|
+
page.should have_no_css "li a[href='#{item["Link Target"]}']", :text => item["Label"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: The assign_to tag
|
2
|
+
As a designer
|
3
|
+
I want to assign a variable in my template
|
4
|
+
So that I can use it multiple times later
|
5
|
+
|
6
|
+
Scenario: Assigning the variable
|
7
|
+
Given this template exists at "assign.nou.html"
|
8
|
+
"""
|
9
|
+
{{ "Hello" | assign_to: "hello" }}
|
10
|
+
<h1>{{ hello }}</h1>
|
11
|
+
"""
|
12
|
+
And this content item exists at "/assign"
|
13
|
+
| template | assign |
|
14
|
+
When I view "/assign"
|
15
|
+
Then the headline should be "Hello"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Feature: Including content in a template
|
2
|
+
As a designer
|
3
|
+
I use content from elsewhere in the repository
|
4
|
+
So that it can be changed by the end user
|
5
|
+
|
6
|
+
Scenario: The content item exists
|
7
|
+
Given this template exists at "inclusion.nou.html"
|
8
|
+
"""
|
9
|
+
{{ "/includes/header" | item_at_path | assign_to: "header" }}
|
10
|
+
<h1>{{ header.title }}</h1>
|
11
|
+
"""
|
12
|
+
And this content item exists at "/inclusion"
|
13
|
+
| template | inclusion |
|
14
|
+
And this content item exists at "/includes/header"
|
15
|
+
| title | The site title |
|
16
|
+
When I view "/inclusion"
|
17
|
+
Then the headline should be "The site title"
|
18
|
+
|
19
|
+
Scenario: The content item does not exist
|
20
|
+
Given this template exists at "missing_content.nou.html"
|
21
|
+
"""
|
22
|
+
{{ "/includes/header" | item_at_path | assign_to: "header" }}
|
23
|
+
{% if header %}
|
24
|
+
<h1>Header Found</h1>
|
25
|
+
{% else %}
|
26
|
+
<h1>No Header</h1>
|
27
|
+
{% endif %}
|
28
|
+
"""
|
29
|
+
And this content item exists at "/missing_content"
|
30
|
+
| template | missing_content |
|
31
|
+
And no content item exists at "/includes/header"
|
32
|
+
When I view "/missing_content"
|
33
|
+
Then the headline should be "No Header"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
Feature: Navigation menus
|
2
|
+
As a designer
|
3
|
+
I want to build a navigation menu from the content repository
|
4
|
+
So that my users can add new pages
|
5
|
+
|
6
|
+
Scenario: Basic menu
|
7
|
+
Given this template exists at "basic_menu.nou.html"
|
8
|
+
"""
|
9
|
+
<nav>
|
10
|
+
{% nav_menu %}
|
11
|
+
</nav>
|
12
|
+
"""
|
13
|
+
And this content item exists at "/about"
|
14
|
+
| title | About |
|
15
|
+
| template | basic_menu |
|
16
|
+
And this content item exists at "/contact"
|
17
|
+
| title | Contact |
|
18
|
+
And this content item exists at "/team"
|
19
|
+
| title | Team |
|
20
|
+
When I view "/about"
|
21
|
+
Then a menu with the following items should be visible:
|
22
|
+
| Label | Link Target |
|
23
|
+
| About | /about |
|
24
|
+
| Contact | /contact |
|
25
|
+
| Team | /team |
|
26
|
+
|
27
|
+
Scenario: Multi-level menu
|
28
|
+
Given this template exists at "deep_menu.nou.html"
|
29
|
+
"""
|
30
|
+
<nav>
|
31
|
+
{% nav_menu depth=2 %}
|
32
|
+
</nav>
|
33
|
+
"""
|
34
|
+
And this content item exists at "/about"
|
35
|
+
| title | About |
|
36
|
+
| template | deep_menu |
|
37
|
+
And this content item exists at "/about/team"
|
38
|
+
| title | Team |
|
39
|
+
And this content item exists at "/contact"
|
40
|
+
| title | Contact |
|
41
|
+
And this content item exists at "/team"
|
42
|
+
| title | Team |
|
43
|
+
When I view "/about"
|
44
|
+
Then a menu with the following items should be visible:
|
45
|
+
| Label | Link Target |
|
46
|
+
| About | /about |
|
47
|
+
| Contact | /contact |
|
48
|
+
| Team | /team |
|
49
|
+
And the "About" menu item should have these children:
|
50
|
+
| Label | Link Target |
|
51
|
+
| Team | /about/team |
|
52
|
+
|
53
|
+
Scenario: Non-root menus
|
54
|
+
Given this template exists at "deep_menu.nou.html"
|
55
|
+
"""
|
56
|
+
<nav>
|
57
|
+
{% nav_menu root=/about %}
|
58
|
+
</nav>
|
59
|
+
"""
|
60
|
+
And this content item exists at "/about"
|
61
|
+
| title | About |
|
62
|
+
| template | deep_menu |
|
63
|
+
And this content item exists at "/about/team"
|
64
|
+
| title | Team |
|
65
|
+
And this content item exists at "/contact"
|
66
|
+
| title | Contact |
|
67
|
+
And this content item exists at "/team"
|
68
|
+
| title | Team |
|
69
|
+
When I view "/about"
|
70
|
+
Then a menu with the following items should be visible:
|
71
|
+
| Label | Link Target |
|
72
|
+
| Team | /about/team |
|
73
|
+
And these menu items should not be visible:
|
74
|
+
| Label | Link Target |
|
75
|
+
| About | /about |
|
76
|
+
| Contact | /contact |
|
77
|
+
| Team | /team |
|
@@ -56,6 +56,29 @@ class Noumenon::Repository::FileSystem < Noumenon::Repository
|
|
56
56
|
path = File.join(path, "index") if File.exist?(repository_path(path))
|
57
57
|
File.open(repository_path("#{path}.yml"), "w") { |f| f.print content.symbolize_keys.to_yaml }
|
58
58
|
end
|
59
|
+
|
60
|
+
def children(root = "/", depth = 1)
|
61
|
+
root.gsub! /\/$/, ''
|
62
|
+
|
63
|
+
pattern = File.join(repository_path(root), "*")
|
64
|
+
items = Dir.glob(pattern).collect do |i|
|
65
|
+
i.gsub(repository_path("/"), "/").
|
66
|
+
gsub(".yml", "")
|
67
|
+
end
|
68
|
+
|
69
|
+
# We're not interested in indexes, since they're not children.
|
70
|
+
items.delete(File.join(root, "index"))
|
71
|
+
|
72
|
+
items.reject { |i| i.gsub(root, "").split("/").size > depth + 1 }.collect do |item|
|
73
|
+
path = item == "" ? "/" : item
|
74
|
+
|
75
|
+
# Loading everyitem probably isn't scalable, but it'll do for now. We can add caching
|
76
|
+
# at a later date if needed.
|
77
|
+
page = get(path).merge({ path: path })
|
78
|
+
page[:children] = children(page[:path], depth - 1) if depth - 1 > 0
|
79
|
+
page
|
80
|
+
end
|
81
|
+
end
|
59
82
|
|
60
83
|
private
|
61
84
|
# Return the on-disk path to a repository item.
|
data/lib/noumenon/repository.rb
CHANGED
@@ -36,4 +36,8 @@ class Noumenon::Repository
|
|
36
36
|
def get(path)
|
37
37
|
raise NotImplementedError.new("This repository type does not support reading it's contents.")
|
38
38
|
end
|
39
|
+
|
40
|
+
def children(root = "/")
|
41
|
+
raise NotImplementedError.new("This repository type does not support listing children from a path.")
|
42
|
+
end
|
39
43
|
end
|
@@ -2,7 +2,7 @@ require 'noumenon/template'
|
|
2
2
|
|
3
3
|
# Liquid extensions provided by Noumenon.
|
4
4
|
#
|
5
|
-
# @see
|
5
|
+
# @see http://github.com/Noumenon/noumenon/wiki/Template%20Tags
|
6
6
|
module Noumenon::Template::CoreTags
|
7
7
|
# The core set of filters provided by Noumenon for use in templates.
|
8
8
|
#
|
@@ -39,4 +39,54 @@ module Noumenon::Template::CoreTags
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
Liquid::Template.register_filter(Filters)
|
42
|
+
|
43
|
+
# Renders a navigation menu.
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
# @example
|
47
|
+
# <header>
|
48
|
+
# {{ nav_menu }}
|
49
|
+
# </header>
|
50
|
+
#
|
51
|
+
# <div id="sidebar">
|
52
|
+
# <h1>About Our Stuff</h1>
|
53
|
+
# <nav>
|
54
|
+
# {{ nav_menu root=/about depth=1 }}
|
55
|
+
# </nav>
|
56
|
+
# </div>
|
57
|
+
class NavigationTag < Liquid::Tag
|
58
|
+
def initialize(tag_name, syntax, tokens)
|
59
|
+
super
|
60
|
+
|
61
|
+
@options = { depth: 1, root: "/" }
|
62
|
+
|
63
|
+
syntax.split(" ").each do |option|
|
64
|
+
key, value = option.split("=")
|
65
|
+
case key
|
66
|
+
when "depth"
|
67
|
+
@options[:depth] = value.to_i
|
68
|
+
when "root"
|
69
|
+
@options[:root] = value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def render_list(items)
|
75
|
+
list = items.collect do |item|
|
76
|
+
str = "<li>"
|
77
|
+
str << %Q{<a href="#{item[:path]}">#{item[:title]}</a>}
|
78
|
+
str << render_list(item[:children]) if item[:children]
|
79
|
+
str << "</li>"
|
80
|
+
|
81
|
+
str
|
82
|
+
end
|
83
|
+
|
84
|
+
"<ul>#{list.join("\n")}</ul>"
|
85
|
+
end
|
86
|
+
|
87
|
+
def render(context)
|
88
|
+
render_list Noumenon.content_repository.children(@options[:root], @options[:depth])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
Liquid::Template.register_tag('nav_menu', NavigationTag)
|
42
92
|
end
|
data/lib/noumenon/version.rb
CHANGED
@@ -112,4 +112,62 @@ describe Noumenon::Repository::FileSystem do
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
end
|
115
|
+
|
116
|
+
describe "listing child items" do
|
117
|
+
before do
|
118
|
+
subject.put("/", title: "Home")
|
119
|
+
subject.put("/about", title: "About")
|
120
|
+
subject.put("/about/team", title: "Team")
|
121
|
+
subject.put("/about/location", title: "Location")
|
122
|
+
subject.put("/contact", title: "Contact")
|
123
|
+
end
|
124
|
+
|
125
|
+
context "a single level from the root" do
|
126
|
+
it "returns an array" do
|
127
|
+
subject.children.should be_instance_of Array
|
128
|
+
end
|
129
|
+
|
130
|
+
it "does not list items below the top level" do
|
131
|
+
subject.children("/").should have(2).items
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "each item" do
|
135
|
+
let(:items) { subject.children("/") }
|
136
|
+
|
137
|
+
it "is a hash" do
|
138
|
+
items[0].should be_instance_of Hash
|
139
|
+
end
|
140
|
+
|
141
|
+
it "has a path" do
|
142
|
+
items[0][:path].should == "/about"
|
143
|
+
items[1][:path].should == "/contact"
|
144
|
+
end
|
145
|
+
|
146
|
+
it "has a title" do
|
147
|
+
items[0][:title].should == "About"
|
148
|
+
items[1][:title].should == "Contact"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "multiple levels from the root" do
|
154
|
+
let(:items) { subject.children("/", 2) }
|
155
|
+
|
156
|
+
describe "an item with children" do
|
157
|
+
it "has an array of children" do
|
158
|
+
items[0][:children].should have(2).items
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "from a point below the root" do
|
164
|
+
let(:items) { subject.children("/about") }
|
165
|
+
|
166
|
+
it "lists only items below that point" do
|
167
|
+
items.should have(2).items
|
168
|
+
items[0][:title].should == "Location"
|
169
|
+
items[1][:title].should == "Team"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
115
173
|
end
|
@@ -37,4 +37,19 @@ describe Noumenon::Repository do
|
|
37
37
|
end
|
38
38
|
end
|
39
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
|
54
|
+
end
|
40
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.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jon Wood
|
@@ -165,20 +165,23 @@ files:
|
|
165
165
|
- README.md
|
166
166
|
- Rakefile
|
167
167
|
- bin/noumenon
|
168
|
-
- features/dynamic_template_rendering.feature
|
169
168
|
- features/generator/site_generator.feature
|
170
|
-
- features/including_content_from_a_template.feature
|
171
169
|
- features/mounted_applications.feature
|
172
|
-
- features/static_template_rendering.feature
|
173
170
|
- features/step_definitions/asset_steps.rb
|
174
171
|
- features/step_definitions/content_steps.rb
|
175
172
|
- features/step_definitions/generator_steps.rb
|
176
173
|
- features/step_definitions/liquid_steps.rb
|
174
|
+
- features/step_definitions/navigation_steps.rb
|
177
175
|
- features/step_definitions/request_steps.rb
|
178
176
|
- features/step_definitions/theme_steps.rb
|
179
177
|
- features/support/env.rb
|
180
178
|
- features/support/theme/theme.yml
|
181
179
|
- features/template_extensions.feature
|
180
|
+
- features/template_rendering/dynamic.feature
|
181
|
+
- features/template_rendering/static.feature
|
182
|
+
- features/template_tags/assign_to.feature
|
183
|
+
- features/template_tags/content_from.feature
|
184
|
+
- features/template_tags/nav_menu.feature
|
182
185
|
- features/theme_assets.feature
|
183
186
|
- generators/repository/index.yml
|
184
187
|
- generators/site/Gemfile
|
@@ -241,20 +244,23 @@ signing_key:
|
|
241
244
|
specification_version: 3
|
242
245
|
summary: A flexible content management system.
|
243
246
|
test_files:
|
244
|
-
- features/dynamic_template_rendering.feature
|
245
247
|
- features/generator/site_generator.feature
|
246
|
-
- features/including_content_from_a_template.feature
|
247
248
|
- features/mounted_applications.feature
|
248
|
-
- features/static_template_rendering.feature
|
249
249
|
- features/step_definitions/asset_steps.rb
|
250
250
|
- features/step_definitions/content_steps.rb
|
251
251
|
- features/step_definitions/generator_steps.rb
|
252
252
|
- features/step_definitions/liquid_steps.rb
|
253
|
+
- features/step_definitions/navigation_steps.rb
|
253
254
|
- features/step_definitions/request_steps.rb
|
254
255
|
- features/step_definitions/theme_steps.rb
|
255
256
|
- features/support/env.rb
|
256
257
|
- features/support/theme/theme.yml
|
257
258
|
- features/template_extensions.feature
|
259
|
+
- features/template_rendering/dynamic.feature
|
260
|
+
- features/template_rendering/static.feature
|
261
|
+
- features/template_tags/assign_to.feature
|
262
|
+
- features/template_tags/content_from.feature
|
263
|
+
- features/template_tags/nav_menu.feature
|
258
264
|
- features/theme_assets.feature
|
259
265
|
- spec/noumenon/repository/file_system_spec.rb
|
260
266
|
- spec/noumenon/repository_spec.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
Feature: Including content in a template
|
2
|
-
As a designer
|
3
|
-
I want to include a piece of content within a page
|
4
|
-
So that it can be changed by the end user
|
5
|
-
|
6
|
-
Scenario: The content item exists
|
7
|
-
Given this template exists at "inclusion.nou.html"
|
8
|
-
"""
|
9
|
-
{{ "/includes/header" | item_at_path | assign_to: "header" }}
|
10
|
-
<h1>{{ header.title }}</h1>
|
11
|
-
"""
|
12
|
-
And this content item exists at "/inclusion"
|
13
|
-
| template | inclusion |
|
14
|
-
And this content item exists at "/includes/header"
|
15
|
-
| title | The site title |
|
16
|
-
When I view "/inclusion"
|
17
|
-
Then the headline should be "The site title"
|
File without changes
|
File without changes
|