noumenon 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|