radiant-taggable-extension 1.2.5 → 2.0.0.rc1
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/README.md +82 -18
- data/Rakefile +0 -15
- data/app/helpers/taggable_helper.rb +18 -0
- data/app/models/library_page.rb +60 -0
- data/app/models/tag.rb +19 -2
- data/app/views/admin/assets/_edit_metadata.html.haml +20 -0
- data/app/views/admin/assets/_edit_title.html.haml +14 -0
- data/app/views/admin/tags/_show_assets.html.haml +22 -0
- data/lib/link_renderer.rb +22 -0
- data/lib/radiant-taggable-extension.rb +8 -0
- data/lib/radius/asset_tags.rb +151 -0
- data/lib/radius/library_tags.rb +387 -0
- data/lib/radius/taggable_tags.rb +655 -0
- data/lib/taggable/admin_pages_controller.rb +16 -0
- data/lib/taggable/admin_ui.rb +43 -0
- data/lib/taggable/asset.rb +37 -0
- data/lib/taggable/model.rb +144 -0
- data/lib/taggable/page.rb +54 -0
- data/lib/taggable/site_controller.rb +33 -0
- data/radiant-taggable-extension.gemspec +23 -85
- data/spec/controllers/site_controller_spec.rb +96 -0
- data/spec/datasets/tags_dataset.rb +2 -0
- data/spec/lib/taggable_page_spec.rb +1 -1
- data/spec/models/library_page_spec.rb +47 -0
- data/taggable_extension.rb +17 -11
- metadata +49 -49
- data/.gitignore +0 -1
- data/VERSION +0 -1
- data/lib/taggable_admin_page_controller.rb +0 -18
- data/lib/taggable_admin_ui.rb +0 -41
- data/lib/taggable_model.rb +0 -139
- data/lib/taggable_page.rb +0 -51
- data/lib/taggable_tags.rb +0 -529
@@ -0,0 +1,43 @@
|
|
1
|
+
module Taggable
|
2
|
+
module AdminUI
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
|
7
|
+
attr_accessor :tag
|
8
|
+
alias_method :tags, :tag
|
9
|
+
|
10
|
+
def load_default_regions_with_tags
|
11
|
+
load_default_regions_without_tags
|
12
|
+
@tag = load_default_tag_regions
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method_chain :load_default_regions, :tags
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def load_default_tag_regions
|
20
|
+
returning OpenStruct.new do |tag|
|
21
|
+
tag.edit = Radiant::AdminUI::RegionSet.new do |edit|
|
22
|
+
edit.main.concat %w{edit_header edit_form}
|
23
|
+
edit.form.concat %w{edit_name edit_role edit_description}
|
24
|
+
edit.form_bottom.concat %w{edit_timestamp edit_buttons}
|
25
|
+
end
|
26
|
+
tag.show = Radiant::AdminUI::RegionSet.new do |show|
|
27
|
+
show.main.concat %w{show_header show_pages show_assets}
|
28
|
+
end
|
29
|
+
tag.index = Radiant::AdminUI::RegionSet.new do |index|
|
30
|
+
index.thead.concat %w{title_header link_header description_header usage_header modify_header}
|
31
|
+
index.tbody.concat %w{title_cell link_cell description_cell usage_cell modify_cell}
|
32
|
+
index.bottom.concat %w{new_button}
|
33
|
+
end
|
34
|
+
tag.remove = tag.index
|
35
|
+
tag.new = tag.edit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Taggable
|
2
|
+
module Asset
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval {
|
6
|
+
has_tags
|
7
|
+
named_scope :furniture, {:conditions => 'assets.furniture = 1'}
|
8
|
+
named_scope :not_furniture, {:conditions => 'assets.furniture = 0 or assets.furniture is null'}
|
9
|
+
|
10
|
+
extend Taggable::Asset::ClassMethods
|
11
|
+
include Taggable::Asset::InstanceMethods
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
|
20
|
+
# just keeping compatibility with page tags
|
21
|
+
# so as to present the same interface
|
22
|
+
|
23
|
+
def keywords
|
24
|
+
self.attached_tags.map {|t| t.title}.join(', ')
|
25
|
+
end
|
26
|
+
|
27
|
+
def keywords=(somewords="")
|
28
|
+
self.attached_tags = Tag.from_list(somewords)
|
29
|
+
end
|
30
|
+
|
31
|
+
def keywords_before_type_cast # called by form_helper
|
32
|
+
keywords
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Taggable
|
2
|
+
module Model # for inclusion into ActiveRecord::Base
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.class_eval {
|
7
|
+
@@taggable_models = []
|
8
|
+
cattr_accessor :taggable_models
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def has_tags?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_tags
|
18
|
+
return if has_tags?
|
19
|
+
|
20
|
+
has_many :taggings, :as => :tagged
|
21
|
+
has_many :attached_tags, :through => :taggings, :source => :tag # can't be just has_many :tags because that stomps on the radius tags in Page.
|
22
|
+
|
23
|
+
named_scope :from_tag, lambda { |tag|
|
24
|
+
tag = Tag.find_by_title(tag) unless tag.is_a? Tag
|
25
|
+
{
|
26
|
+
:joins => "INNER JOIN taggings as tt on tt.tagged_id = #{self.table_name}.id AND tt.tagged_type = '#{self.to_s}'",
|
27
|
+
:conditions => ["tt.tag_id = ?", tag.id],
|
28
|
+
:readonly => false
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
named_scope :from_tags, lambda { |tags|
|
33
|
+
{
|
34
|
+
:joins => "INNER JOIN taggings as tt on tt.tagged_id = #{self.table_name}.id AND tt.tagged_type = '#{self.to_s}'",
|
35
|
+
:conditions => ["tt.tag_id in(#{tags.map{ '?' }.join(',')})"] + tags.map(&:id),
|
36
|
+
:group => column_names.map { |n| table_name + '.' + n }.join(','), # postgres is strict and requires that we group by all selected (but not aggregated) columns
|
37
|
+
:order => "count(tt.id) DESC",
|
38
|
+
:readonly => false
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
named_scope :from_all_tags, lambda { |tags|
|
43
|
+
{
|
44
|
+
:joins => "INNER JOIN taggings as tt on tt.tagged_id = #{self.table_name}.id AND tt.tagged_type = '#{self.to_s}'",
|
45
|
+
:conditions => ["tt.tag_id in(#{tags.map{ '?' }.join(',')})"] + tags.map(&:id),
|
46
|
+
:group => column_names.map { |n| table_name + '.' + n }.join(','), # postgres is strict and requires that we group by all selected (but not aggregated) columns
|
47
|
+
:having => "count(tt.id) >= #{tags.length}",
|
48
|
+
:readonly => false
|
49
|
+
}
|
50
|
+
} do
|
51
|
+
# count is badly sugared here: it omits the group and having clauses.
|
52
|
+
# length performs the query and looks at the array: less neat, but more right
|
53
|
+
# this gives us back any? and empty? as well.
|
54
|
+
def count
|
55
|
+
length
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# creates eg. tag.pages, tag.assets
|
60
|
+
# (returning the from_tag scope defined above)
|
61
|
+
Tag.define_retrieval_methods(self.to_s)
|
62
|
+
|
63
|
+
class_eval {
|
64
|
+
extend Taggable::Model::TaggableClassMethods
|
65
|
+
include Taggable::Model::TaggableInstanceMethods
|
66
|
+
alias_method "related_#{self.to_s.underscore.pluralize}".intern, :related
|
67
|
+
alias_method "closely_related_#{self.to_s.underscore.pluralize}".intern, :closely_related
|
68
|
+
}
|
69
|
+
|
70
|
+
ActiveRecord::Base.taggable_models.push(self.to_s.intern)
|
71
|
+
end
|
72
|
+
|
73
|
+
alias :is_taggable :has_tags
|
74
|
+
alias :is_taggable? :has_tags?
|
75
|
+
end
|
76
|
+
|
77
|
+
module TaggableClassMethods
|
78
|
+
def tagged_with(somewords=[])
|
79
|
+
if somewords.is_a?(Tag)
|
80
|
+
self.from_tag(somewords)
|
81
|
+
elsif somewords.is_a?(Array)
|
82
|
+
self.from_all_tags(somewords)
|
83
|
+
else
|
84
|
+
self.from_all_tags( Tag.from_list(somewords) )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_tags?
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
def tags_for_cloud_from(these, limit=50)
|
93
|
+
Tag.attached_to(these).most_popular(limit) # here popularity is use-count *within the group*
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
module TaggableInstanceMethods
|
98
|
+
|
99
|
+
def add_tag(word=nil)
|
100
|
+
self.attached_tags << Tag.for(word) if word && !word.blank?
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_tag(word=nil)
|
104
|
+
tag = Tag.find_by_title(word) if word && !word.blank?
|
105
|
+
self.attached_tags.delete(tag) if tag
|
106
|
+
end
|
107
|
+
|
108
|
+
def related
|
109
|
+
self.attached_tags.empty? ? [] : self.class.from_tags(self.attached_tags) - [self]
|
110
|
+
end
|
111
|
+
|
112
|
+
def closely_related
|
113
|
+
self.attached_tags.empty? ? [] : self.class.from_all_tags(self.attached_tags) - [self]
|
114
|
+
end
|
115
|
+
|
116
|
+
# in the case of pages and anything else that keywords in the same way this overrides the existing column
|
117
|
+
# the rest of the time it's just another way of specifying tags.
|
118
|
+
|
119
|
+
def keywords
|
120
|
+
self.attached_tags.map {|t| t.title}.join(', ')
|
121
|
+
end
|
122
|
+
|
123
|
+
def keywords=(somewords="")
|
124
|
+
if somewords.blank?
|
125
|
+
self.attached_tags.clear
|
126
|
+
else
|
127
|
+
self.attached_tags = Tag.from_list(somewords)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def keywords_before_type_cast # for form_helper
|
132
|
+
keywords
|
133
|
+
end
|
134
|
+
|
135
|
+
def tags_from_keywords
|
136
|
+
if self.class.column_names.include?('keywords') && keys = read_attribute(:keywords)
|
137
|
+
self.attached_tags = Tag.from_list(keys)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Taggable
|
2
|
+
module Page # for inclusion into Page
|
3
|
+
|
4
|
+
# here we have a few special cases for page tags.
|
5
|
+
# because of the page tree
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval {
|
9
|
+
has_tags
|
10
|
+
has_one :pointer, :class_name => 'Tag'
|
11
|
+
named_scope :children_of, lambda { |these|
|
12
|
+
{ :conditions => ["parent_id IN (#{these.map{'?'}.join(',')})", *these.map{|t| t.id}] }
|
13
|
+
}
|
14
|
+
extend Taggable::Page::ClassMethods
|
15
|
+
include Taggable::Page::InstanceMethods
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
|
24
|
+
def has_pointer?
|
25
|
+
!pointer.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
# note varying logic here: tag clouds are used differently when describing a group.
|
29
|
+
# if only one object is relevant, all of its tags will be equally (locally) important.
|
30
|
+
# Presumably that cloud should show global tag importance.
|
31
|
+
# If several objects are relevant, either from a list or a tree of descendants, we
|
32
|
+
# probably want to show local tag importance, ie prominence within that list.
|
33
|
+
|
34
|
+
def tags_for_cloud(limit=50, bands=6)
|
35
|
+
tags = Tag.attached_to(self.with_children).visible.most_popular(limit)
|
36
|
+
Tag.sized(tags, bands)
|
37
|
+
end
|
38
|
+
|
39
|
+
# the family-tree builder works with generations instead of individuals to cut down the number of retrieval steps
|
40
|
+
|
41
|
+
def with_children
|
42
|
+
this_generation = [self]
|
43
|
+
return this_generation unless self.respond_to?(:children) && self.children.any?
|
44
|
+
family = [self]
|
45
|
+
while this_generation.any? && next_generation = self.class.children_of(this_generation)
|
46
|
+
family.push(*next_generation)
|
47
|
+
this_generation = next_generation
|
48
|
+
end
|
49
|
+
family
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Taggable
|
2
|
+
module SiteController
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
|
6
|
+
base.class_eval {
|
7
|
+
|
8
|
+
def find_page_with_tags(url)
|
9
|
+
url = clean_url(url)
|
10
|
+
page = find_page_without_tags(url)
|
11
|
+
return page unless page.is_a?(LibraryPage)
|
12
|
+
page.add_request_tags(Tag.in_this_list(params[:tag])) if params[:tag]
|
13
|
+
raise LibraryPage::RedirectRequired, page.url unless page.url == url # to handle removal of tags and ensure consistent addressing. should also allow cache hit.
|
14
|
+
page
|
15
|
+
end
|
16
|
+
alias_method_chain :find_page, :tags
|
17
|
+
|
18
|
+
def show_page_with_tags
|
19
|
+
show_page_without_tags
|
20
|
+
rescue LibraryPage::RedirectRequired => e
|
21
|
+
redirect_to e.message
|
22
|
+
end
|
23
|
+
alias_method_chain :show_page, :tags
|
24
|
+
|
25
|
+
protected
|
26
|
+
def clean_url(url)
|
27
|
+
"/#{ url.strip }/".gsub(%r{//+}, '/')
|
28
|
+
end
|
29
|
+
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,93 +1,31 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "radiant-taggable-extension"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
6
|
+
s.name = "radiant-taggable-extension"
|
7
|
+
s.version = RadiantTaggableExtension::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = RadiantTaggableExtension::AUTHORS
|
10
|
+
s.email = RadiantTaggableExtension::EMAIL
|
11
|
+
s.homepage = RadiantTaggableExtension::URL
|
12
|
+
s.summary = RadiantTaggableExtension::SUMMARY
|
13
|
+
s.description = RadiantTaggableExtension::DESCRIPTION
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
s.
|
16
|
-
|
17
|
-
]
|
18
|
-
s.files = [
|
19
|
-
".gitignore",
|
20
|
-
"README.md",
|
21
|
-
"Rakefile",
|
22
|
-
"VERSION",
|
23
|
-
"app/controllers/admin/taggings_controller.rb",
|
24
|
-
"app/controllers/admin/tags_controller.rb",
|
25
|
-
"app/helpers/taggable_helper.rb",
|
26
|
-
"app/models/tag.rb",
|
27
|
-
"app/models/tagging.rb",
|
28
|
-
"app/views/admin/pages/_edit_title.html.haml",
|
29
|
-
"app/views/admin/tags/_form.html.haml",
|
30
|
-
"app/views/admin/tags/_search_results.html.haml",
|
31
|
-
"app/views/admin/tags/cloud.html.haml",
|
32
|
-
"app/views/admin/tags/edit.html.haml",
|
33
|
-
"app/views/admin/tags/index.html.haml",
|
34
|
-
"app/views/admin/tags/new.html.haml",
|
35
|
-
"app/views/admin/tags/show.html.haml",
|
36
|
-
"config/locales/en.yml",
|
37
|
-
"config/routes.rb",
|
38
|
-
"db/migrate/001_create_tags.rb",
|
39
|
-
"db/migrate/002_import_keywords.rb",
|
40
|
-
"db/migrate/20110316210834_structural_tags.rb",
|
41
|
-
"db/migrate/20110411075109_metaphones.rb",
|
42
|
-
"lib/natcmp.rb",
|
43
|
-
"lib/radiant-taggable-extension.rb",
|
44
|
-
"lib/taggable_admin_page_controller.rb",
|
45
|
-
"lib/taggable_admin_ui.rb",
|
46
|
-
"lib/taggable_model.rb",
|
47
|
-
"lib/taggable_page.rb",
|
48
|
-
"lib/taggable_tags.rb",
|
49
|
-
"lib/tasks/taggable_extension_tasks.rake",
|
50
|
-
"lib/text/double_metaphone.rb",
|
51
|
-
"lib/text/metaphone.rb",
|
52
|
-
"public/images/admin/new-tag.png",
|
53
|
-
"public/images/admin/tag.png",
|
54
|
-
"public/javascripts/admin/taggable.js",
|
55
|
-
"public/javascripts/autocomplete.js",
|
56
|
-
"public/stylesheets/sass/admin/taggable.sass",
|
57
|
-
"public/stylesheets/sass/tagcloud.sass",
|
58
|
-
"radiant-taggable-extension.gemspec",
|
59
|
-
"spec/datasets/tag_sites_dataset.rb",
|
60
|
-
"spec/datasets/tags_dataset.rb",
|
61
|
-
"spec/lib/taggable_page_spec.rb",
|
62
|
-
"spec/models/tag_spec.rb",
|
63
|
-
"spec/spec.opts",
|
64
|
-
"spec/spec_helper.rb",
|
65
|
-
"taggable_extension.rb"
|
66
|
-
]
|
67
|
-
s.homepage = %q{http://github.com/spanner/radiant-taggable-extension}
|
68
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
15
|
+
ignores = if File.exist?('.gitignore')
|
16
|
+
File.read('.gitignore').split("\n").inject([]) {|a,p| a + Dir[p] }
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
s.files = Dir['**/*'] - ignores
|
21
|
+
s.test_files = Dir['test/**/*','spec/**/*','features/**/*'] - ignores
|
22
|
+
# s.executables = Dir['bin/*'] - ignores
|
69
23
|
s.require_paths = ["lib"]
|
70
|
-
s.rubygems_version = %q{1.3.7}
|
71
|
-
s.summary = %q{Taggable Extension for Radiant CMS}
|
72
|
-
s.test_files = [
|
73
|
-
"spec/datasets/tag_sites_dataset.rb",
|
74
|
-
"spec/datasets/tags_dataset.rb",
|
75
|
-
"spec/lib/taggable_page_spec.rb",
|
76
|
-
"spec/models/tag_spec.rb",
|
77
|
-
"spec/spec_helper.rb"
|
78
|
-
]
|
79
24
|
|
80
|
-
|
81
|
-
|
82
|
-
s.specification_version = 3
|
25
|
+
s.post_install_message = %{
|
26
|
+
Add this to your radiant project with:
|
83
27
|
|
84
|
-
|
85
|
-
s.add_runtime_dependency(%q<radiant>, [">= 0.9.0"])
|
86
|
-
else
|
87
|
-
s.add_dependency(%q<radiant>, [">= 0.9.0"])
|
88
|
-
end
|
89
|
-
else
|
90
|
-
s.add_dependency(%q<radiant>, [">= 0.9.0"])
|
91
|
-
end
|
92
|
-
end
|
28
|
+
config.gem 'radiant-taggable-extension', :version => '~> #{RadiantTaggableExtension::VERSION}'
|
93
29
|
|
30
|
+
}
|
31
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe SiteController do
|
4
|
+
dataset :tags
|
5
|
+
|
6
|
+
describe "on get to a library page" do
|
7
|
+
before do
|
8
|
+
get :show_page, :url => '/library/'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should render the tag page" do
|
12
|
+
response.should be_success
|
13
|
+
response.body.should == 'Shhhhh. body.'
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "with tags in child position" do
|
17
|
+
before do
|
18
|
+
get :show_page, :url => '/library/colourless/green/'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should still render the tag page" do
|
22
|
+
response.should be_success
|
23
|
+
response.body.should == 'Shhhhh. body.'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "with tags in child position and missing final /" do
|
28
|
+
before do
|
29
|
+
get :show_page, :url => '/library/colourless/green'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should still render the tag page" do
|
33
|
+
response.should be_success
|
34
|
+
response.body.should == 'Shhhhh. body.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "with tag negation" do
|
39
|
+
before do
|
40
|
+
get :show_page, :url => '/library/colourless/green/-colourless'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should redirect to the reduced address" do
|
44
|
+
response.should be_redirect
|
45
|
+
response.should redirect_to('http://test.host/library/green/')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "caching" do
|
51
|
+
describe "without tags requested" do
|
52
|
+
it "should add a default Cache-Control header with public and max-age of 5 minutes" do
|
53
|
+
get :show_page, :url => '/library/'
|
54
|
+
response.headers['Cache-Control'].should =~ /public/
|
55
|
+
response.headers['Cache-Control'].should =~ /max-age=300/
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should pass along the etag set by the page" do
|
59
|
+
get :show_page, :url => '/library/'
|
60
|
+
response.headers['ETag'].should be
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should return a not-modified response when the sent etag matches" do
|
64
|
+
response.stub!(:etag).and_return("foobar")
|
65
|
+
request.if_none_match = 'foobar'
|
66
|
+
get :show_page, :url => '/library/'
|
67
|
+
response.response_code.should == 304
|
68
|
+
response.body.should be_blank
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "with tags requested" do
|
73
|
+
it "should add a default Cache-Control header with public and max-age of 5 minutes" do
|
74
|
+
get :show_page, :url => '/library/green/furiously'
|
75
|
+
response.headers['Cache-Control'].should =~ /public/
|
76
|
+
response.headers['Cache-Control'].should =~ /max-age=300/
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should pass along the etag set by the page" do
|
80
|
+
get :show_page, :url => '/library/green/furiously'
|
81
|
+
response.headers['ETag'].should be
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return a not-modified response when the sent etag matches" do
|
85
|
+
response.stub!(:etag).and_return("foobar")
|
86
|
+
request.if_none_match = 'foobar'
|
87
|
+
get :show_page, :url => '/library/green/furiously'
|
88
|
+
response.response_code.should == 304
|
89
|
+
response.body.should be_blank
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -16,6 +16,8 @@ class TagsDataset < Dataset::Base
|
|
16
16
|
apply_tag :ideas, pages(:first), pages(:another), pages(:grandchild)
|
17
17
|
apply_tag :sleep, pages(:first)
|
18
18
|
apply_tag :furiously, pages(:first)
|
19
|
+
|
20
|
+
create_page "library", :slug => "library", :class_name => 'LibraryPage', :body => 'Shhhhh.'
|
19
21
|
end
|
20
22
|
|
21
23
|
helpers do
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe LibraryPage do
|
4
|
+
dataset :tags
|
5
|
+
|
6
|
+
it "should be a Page" do
|
7
|
+
page = LibraryPage.new
|
8
|
+
page.is_a?(Page).should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "on request" do
|
12
|
+
describe "with one tag" do
|
13
|
+
before do
|
14
|
+
@page = Page.find_by_url('/library/colourless')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should interrupt find_by_url" do
|
18
|
+
@page.should == pages(:library)
|
19
|
+
@page.is_a?(LibraryPage).should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set tag context correctly" do
|
23
|
+
@page.requested_tags.should == [tags(:colourless)]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "with several tags" do
|
28
|
+
before do
|
29
|
+
@page = Page.find_by_url('/library/colourless/green/ideas')
|
30
|
+
end
|
31
|
+
it "should set tag context correctly" do
|
32
|
+
@page.requested_tags.should == [tags(:colourless), tags(:green), tags(:ideas)]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with several tags and one tag negation" do
|
37
|
+
before do
|
38
|
+
@page = Page.find_by_url('/library/colourless/green/ideas/-green')
|
39
|
+
end
|
40
|
+
it "should set tag context correctly" do
|
41
|
+
@page.requested_tags.should == [tags(:colourless), tags(:ideas)]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/taggable_extension.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
+
require_dependency 'application_controller'
|
2
|
+
require "radiant-taggable-extension"
|
3
|
+
|
1
4
|
class TaggableExtension < Radiant::Extension
|
2
|
-
version
|
3
|
-
description
|
4
|
-
url
|
5
|
+
version RadiantTaggableExtension::VERSION
|
6
|
+
description RadiantTaggableExtension::DESCRIPTION
|
7
|
+
url RadiantTaggableExtension::URL
|
5
8
|
|
6
9
|
def activate
|
7
|
-
require 'natcmp'
|
8
|
-
ActiveRecord::Base.send :include,
|
9
|
-
Page.send :
|
10
|
-
|
11
|
-
Page.send :include, TaggableTags
|
12
|
-
|
13
|
-
|
10
|
+
require 'natcmp' # a natural sort algorithm. possibly not that efficient.
|
11
|
+
ActiveRecord::Base.send :include, Taggable::Model # provide has_tags for everything but don't call it for anything
|
12
|
+
Page.send :include, Taggable::Page # pages are taggable (and the keywords column is overridden)
|
13
|
+
Asset.send :include, Taggable::Asset # assets are taggable (and a fake keywords column is provided)
|
14
|
+
Page.send :include, Radius::TaggableTags # adds the basic radius tags for showing page tags and tag pages
|
15
|
+
Page.send :include, Radius::AssetTags # adds some asset:* tags
|
16
|
+
LibraryPage.send :include, Radius::LibraryTags #
|
17
|
+
SiteController.send :include, Taggable::SiteController # some path and parameter handling in support of library pages
|
18
|
+
Admin::PagesController.send :include, Taggable::AdminPagesController # tweaks the admin interface to make page tags more prominent
|
19
|
+
UserActionObserver.instance.send :add_observer!, Tag # tags get creator-stamped
|
14
20
|
|
15
21
|
unless defined? admin.tag
|
16
|
-
Radiant::AdminUI.send :include,
|
22
|
+
Radiant::AdminUI.send :include, Taggable::AdminUI
|
17
23
|
admin.tag = Radiant::AdminUI.load_default_tag_regions
|
18
24
|
end
|
19
25
|
|