radiant 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of radiant might be problematic. Click here for more details.
- data/CHANGELOG +1 -0
- data/CONTRIBUTORS +12 -0
- data/LICENSE +9 -0
- data/README +91 -0
- data/Rakefile +10 -0
- data/app/behaviors/archive_behavior.rb +42 -0
- data/app/behaviors/archive_day_index_behavior.rb +30 -0
- data/app/behaviors/archive_month_index_behavior.rb +30 -0
- data/app/behaviors/archive_year_index_behavior.rb +30 -0
- data/app/behaviors/env_behavior.rb +18 -0
- data/app/behaviors/page_missing_behavior.rb +31 -0
- data/app/controllers/admin/export_controller.rb +8 -0
- data/app/controllers/admin/layout_controller.rb +23 -0
- data/app/controllers/admin/model_controller.rb +119 -0
- data/app/controllers/admin/page_controller.rb +119 -0
- data/app/controllers/admin/snippet_controller.rb +6 -0
- data/app/controllers/admin/user_controller.rb +45 -0
- data/app/controllers/admin/welcome_controller.rb +39 -0
- data/app/controllers/application.rb +32 -0
- data/app/controllers/site_controller.rb +54 -0
- data/app/filters/markdown_filter.rb +9 -0
- data/app/filters/textile_filter.rb +9 -0
- data/app/helpers/admin/export_helper.rb +2 -0
- data/app/helpers/admin/layout_helper.rb +2 -0
- data/app/helpers/admin/page_helper.rb +10 -0
- data/app/helpers/admin/snippet_helper.rb +2 -0
- data/app/helpers/admin/user_helper.rb +2 -0
- data/app/helpers/admin/welcome_helper.rb +2 -0
- data/app/helpers/application_helper.rb +141 -0
- data/app/helpers/site_helper.rb +2 -0
- data/app/models/archive_finder.rb +66 -0
- data/app/models/behavior.rb +194 -0
- data/app/models/layout.rb +13 -0
- data/app/models/page.rb +91 -0
- data/app/models/page_context.rb +526 -0
- data/app/models/page_part.rb +13 -0
- data/app/models/radiant/config.rb +53 -0
- data/app/models/radiant/exporter.rb +11 -0
- data/app/models/response_cache.rb +112 -0
- data/app/models/snippet.rb +16 -0
- data/app/models/status.rb +31 -0
- data/app/models/text_filter.rb +11 -0
- data/app/models/user.rb +70 -0
- data/app/models/user_action_observer.rb +13 -0
- data/app/views/admin/layout/index.rhtml +38 -0
- data/app/views/admin/layout/new.rhtml +37 -0
- data/app/views/admin/layout/remove.rhtml +17 -0
- data/app/views/admin/page/_node.rhtml +51 -0
- data/app/views/admin/page/_part.rhtml +17 -0
- data/app/views/admin/page/children.rhtml +4 -0
- data/app/views/admin/page/index.rhtml +172 -0
- data/app/views/admin/page/new.rhtml +164 -0
- data/app/views/admin/page/remove.rhtml +14 -0
- data/app/views/admin/snippet/index.rhtml +36 -0
- data/app/views/admin/snippet/new.rhtml +28 -0
- data/app/views/admin/snippet/remove.rhtml +16 -0
- data/app/views/admin/user/index.rhtml +43 -0
- data/app/views/admin/user/new.rhtml +48 -0
- data/app/views/admin/user/preferences.rhtml +29 -0
- data/app/views/admin/user/remove.rhtml +16 -0
- data/app/views/admin/welcome/login.rhtml +51 -0
- data/app/views/layouts/application.rhtml +75 -0
- data/app/views/site/not_found.rhtml +3 -0
- data/bin/radiant +305 -0
- data/config/boot.rb +80 -0
- data/config/database.mysql.yml +20 -0
- data/config/database.postgresql.yml +20 -0
- data/config/database.sqlite.yml +20 -0
- data/config/environment.rb +76 -0
- data/config/environments/development.rb +20 -0
- data/config/environments/production.rb +22 -0
- data/config/environments/test.rb +20 -0
- data/config/locomotive.yml +6 -0
- data/config/routes.rb +64 -0
- data/db/development_structure.sql +80 -0
- data/db/migrate/001_create_radiant_tables.rb +73 -0
- data/db/migrate/002_insert_initial_data.rb +45 -0
- data/db/migrate/003_rename_behavior_column.rb +9 -0
- data/db/migrate/004_rename_filter_column.rb +11 -0
- data/db/migrate/005_add_virtual_column_to_page.rb +9 -0
- data/db/migrate/006_integer_columns_to_boolean.rb +11 -0
- data/db/migrate/007_remove_virtual_column_from_page.rb +9 -0
- data/db/migrate/008_add_virtual_column_to_page_again.rb +9 -0
- data/db/migrate/009_add_content_type_field_to_layout.rb +9 -0
- data/db/schema.rb +74 -0
- data/db/templates/empty.yml +2 -0
- data/db/templates/simple-blog.yml +197 -0
- data/db/templates/styled-blog.yml +472 -0
- data/lib/advanced_delegation.rb +21 -0
- data/lib/archive_index_behavior_tags_and_methods.rb +48 -0
- data/lib/generators/behavior/USAGE +16 -0
- data/lib/generators/behavior/behavior_generator.rb +22 -0
- data/lib/generators/behavior/templates/model.rb.template +9 -0
- data/lib/generators/behavior/templates/unit_test.rb.template +16 -0
- data/lib/generators/filter/USAGE +16 -0
- data/lib/generators/filter/filter_generator.rb +22 -0
- data/lib/generators/filter/templates/model.rb.template +8 -0
- data/lib/generators/filter/templates/unit_test.rb.template +7 -0
- data/lib/inheritable_class_attributes.rb +65 -0
- data/lib/login_system.rb +80 -0
- data/lib/plugins/index_quoting_fix/init.rb +32 -0
- data/lib/plugins/string_io/init.rb +2 -0
- data/lib/radiant.rb +5 -0
- data/lib/registerable.rb +70 -0
- data/lib/tasks/release.rake +84 -0
- data/public/404.html +8 -0
- data/public/500.html +8 -0
- data/public/dispatch.cgi +10 -0
- data/public/dispatch.fcgi +24 -0
- data/public/dispatch.rb +10 -0
- data/public/favicon.ico +0 -0
- data/public/images/add-child.png +0 -0
- data/public/images/brown-bottom-line.gif +0 -0
- data/public/images/clear-page-cache.png +0 -0
- data/public/images/collapse.png +0 -0
- data/public/images/expand.png +0 -0
- data/public/images/minus.png +0 -0
- data/public/images/new-homepage.png +0 -0
- data/public/images/new-layout.png +0 -0
- data/public/images/new-snippet.png +0 -0
- data/public/images/new-user.png +0 -0
- data/public/images/page.png +0 -0
- data/public/images/plus.png +0 -0
- data/public/images/remove-disabled.png +0 -0
- data/public/images/remove.png +0 -0
- data/public/images/snippet.png +0 -0
- data/public/images/spinner.gif +0 -0
- data/public/images/view-site.gif +0 -0
- data/public/images/virtual-page.png +0 -0
- data/public/javascripts/application.js +2 -0
- data/public/javascripts/controls.js +815 -0
- data/public/javascripts/dragdrop.js +913 -0
- data/public/javascripts/effects.js +958 -0
- data/public/javascripts/pngfix.js +78 -0
- data/public/javascripts/prototype.js +2006 -0
- data/public/javascripts/ruledtable.js +28 -0
- data/public/javascripts/string.js +23 -0
- data/public/javascripts/tabcontrol.js +140 -0
- data/public/robots.txt +1 -0
- data/public/stylesheets/admin.css +464 -0
- data/script/about +3 -0
- data/script/breakpointer +3 -0
- data/script/console +3 -0
- data/script/destroy +3 -0
- data/script/generate +3 -0
- data/script/performance/benchmarker +3 -0
- data/script/performance/profiler +3 -0
- data/script/plugin +3 -0
- data/script/process/reaper +3 -0
- data/script/process/spawner +3 -0
- data/script/process/spinner +3 -0
- data/script/runner +3 -0
- data/script/server +3 -0
- data/script/setup_database +297 -0
- data/test/fixtures/layouts.yml +26 -0
- data/test/fixtures/page_parts.yml +99 -0
- data/test/fixtures/pages.yml +359 -0
- data/test/fixtures/pages.yml.rej +28 -0
- data/test/fixtures/snippets.yml +18 -0
- data/test/fixtures/users.yml +30 -0
- data/test/functional/admin/export_controller_test.rb +22 -0
- data/test/functional/admin/layout_controller_test.rb +40 -0
- data/test/functional/admin/model_controller_test.rb +152 -0
- data/test/functional/admin/page_controller_test.rb +179 -0
- data/test/functional/admin/snippet_controller_test.rb +11 -0
- data/test/functional/admin/user_controller_test.rb +71 -0
- data/test/functional/admin/welcome_controller_test.rb +48 -0
- data/test/functional/application_controller_test.rb +44 -0
- data/test/functional/login_system_test.rb +155 -0
- data/test/functional/site_controller_test.rb +172 -0
- data/test/helpers/archive_index_test_helper.rb +35 -0
- data/test/helpers/behavior_render_test_helper.rb +34 -0
- data/test/helpers/behavior_test_helper.rb +47 -0
- data/test/helpers/caching_test_helper.rb +41 -0
- data/test/helpers/layout_test_helper.rb +35 -0
- data/test/helpers/page_part_test_helper.rb +49 -0
- data/test/helpers/page_test_helper.rb +36 -0
- data/test/helpers/snippet_test_helper.rb +32 -0
- data/test/helpers/user_test_helper.rb +34 -0
- data/test/helpers/validation_test_helper.rb +42 -0
- data/test/test_helper.rb +54 -0
- data/test/unit/behavior_test.rb +196 -0
- data/test/unit/behaviors/archive_behavior_test.rb +33 -0
- data/test/unit/behaviors/archive_day_index_behavior_test.rb +21 -0
- data/test/unit/behaviors/archive_month_index_behavior_test.rb +21 -0
- data/test/unit/behaviors/archive_year_index_behavior_test.rb +21 -0
- data/test/unit/behaviors/page_missing_behavior_test.rb +27 -0
- data/test/unit/filters/markdown_filter_test.rb +14 -0
- data/test/unit/filters/textile_filter_test.rb +14 -0
- data/test/unit/inheritable_class_attributes_test.rb +47 -0
- data/test/unit/layout_test.rb +29 -0
- data/test/unit/page_context_test.rb +375 -0
- data/test/unit/page_context_test.rb.rej +26 -0
- data/test/unit/page_part_test.rb +44 -0
- data/test/unit/page_test.rb +224 -0
- data/test/unit/radiant/config_test.rb +46 -0
- data/test/unit/radiant/exporter_test.rb +26 -0
- data/test/unit/registerable_test.rb +68 -0
- data/test/unit/response_cache_test.rb +133 -0
- data/test/unit/snippet_test.rb +47 -0
- data/test/unit/status_test.rb +43 -0
- data/test/unit/text_filter_test.rb +14 -0
- data/test/unit/user_action_observer_test.rb +40 -0
- data/test/unit/user_test.rb +138 -0
- metadata +355 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
class ArchiveFinder
|
2
|
+
def initialize(&block)
|
3
|
+
@block = block
|
4
|
+
end
|
5
|
+
|
6
|
+
def find(method, options = {})
|
7
|
+
@block.call(method, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def year_finder(finder, year)
|
12
|
+
new do |method, options|
|
13
|
+
add_condition(options, "#{extract('year', 'published_at')} = ?", year.to_i)
|
14
|
+
finder.find(method, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def month_finder(finder, year, month)
|
19
|
+
finder = year_finder(finder, year)
|
20
|
+
new do |method, options|
|
21
|
+
add_condition(options, "#{extract('month', 'published_at')} = ?", month.to_i)
|
22
|
+
finder.find(method, options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def day_finder(finder, year, month, day)
|
27
|
+
finder = month_finder(finder, year, month)
|
28
|
+
new do |method, options|
|
29
|
+
add_condition(options, "#{extract('day', 'published_at')} = ?", day.to_i)
|
30
|
+
finder.find(method, options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def concat_conditions(a, b)
|
37
|
+
sql = "(#{ [a.shift, b.shift].compact.join(") AND (") })"
|
38
|
+
params = a + b
|
39
|
+
[sql, *params]
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_condition(options, *condition)
|
43
|
+
old = options[:conditions] || []
|
44
|
+
conditions = concat_conditions(old, condition)
|
45
|
+
options[:conditions] = conditions
|
46
|
+
options
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract(part, field)
|
50
|
+
case ActiveRecord::Base.connection.adapter_name
|
51
|
+
when /sqlite/i
|
52
|
+
case part
|
53
|
+
when /year/i
|
54
|
+
"STRFTIME('%Y', #{field})"
|
55
|
+
when /month/i
|
56
|
+
"STRFTIME('%m', #{field})"
|
57
|
+
when /day/i
|
58
|
+
"STRFTIME('%d', #{field})"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
"EXTRACT(#{part.upcase} FROM #{field})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'registerable'
|
2
|
+
|
3
|
+
module Behavior
|
4
|
+
include Registerable
|
5
|
+
|
6
|
+
class Base
|
7
|
+
|
8
|
+
include InheritableClassAttributes
|
9
|
+
|
10
|
+
cattr_inheritable_reader :additional_tag_definition_blocks
|
11
|
+
@additional_tag_definition_blocks = []
|
12
|
+
|
13
|
+
cattr_inheritable_reader :additional_child_tag_definition_blocks
|
14
|
+
@additional_child_tag_definition_blocks = []
|
15
|
+
|
16
|
+
cattr_inheritable_accessor :description
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def description(value = nil)
|
20
|
+
if value.nil?
|
21
|
+
@description
|
22
|
+
else
|
23
|
+
@description = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_tags(&block)
|
28
|
+
@additional_tag_definition_blocks << block
|
29
|
+
end
|
30
|
+
|
31
|
+
def define_child_tags(&block)
|
32
|
+
@additional_child_tag_definition_blocks << block
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :page, :request, :response
|
37
|
+
|
38
|
+
def initialize(page)
|
39
|
+
@page = page
|
40
|
+
end
|
41
|
+
|
42
|
+
def description
|
43
|
+
self.class.description
|
44
|
+
end
|
45
|
+
|
46
|
+
def process(request, response)
|
47
|
+
@request, @response = request, response
|
48
|
+
if @page.layout
|
49
|
+
content_type = @page.layout.content_type.to_s.strip
|
50
|
+
@response.headers['Content-Type'] = content_type unless content_type.empty?
|
51
|
+
end
|
52
|
+
page_headers.each { |k,v| @response.headers[k] = v }
|
53
|
+
@response.body = render_page
|
54
|
+
@request, @response = nil, nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def page_url
|
58
|
+
if parent_behavior?
|
59
|
+
clean_url(parent_behavior.child_url(@page))
|
60
|
+
else
|
61
|
+
clean_url(@page.slug)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def child_url(child)
|
66
|
+
clean_url(page_url + '/' + child.slug)
|
67
|
+
end
|
68
|
+
|
69
|
+
def page_headers
|
70
|
+
{ 'Status' => ActionController::Base::DEFAULT_RENDER_STATUS_CODE }
|
71
|
+
end
|
72
|
+
|
73
|
+
def page_virtual?
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def page_config
|
78
|
+
string = render_page_part(:config)
|
79
|
+
unless string.empty?
|
80
|
+
YAML::load(string)
|
81
|
+
else
|
82
|
+
{}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def cache_page?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_page_by_url(url, live = true, clean = true)
|
91
|
+
url = clean_url(url) if clean
|
92
|
+
if page_url == url && (not live or @page.published?)
|
93
|
+
@page
|
94
|
+
else
|
95
|
+
@page.children.each do |child|
|
96
|
+
if (url =~ Regexp.compile( '^' + Regexp.quote(child.url))) and (not child.virtual?)
|
97
|
+
found = child.behavior.find_page_by_url(url, live, clean)
|
98
|
+
return found if found
|
99
|
+
end
|
100
|
+
end
|
101
|
+
@page.children.find(:first, :conditions => "behavior_id = 'Page Missing'")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_find_page_by_url__when_virtual
|
106
|
+
setup_for_page(:homepage)
|
107
|
+
found = @behavior.find_page_by_url('/parent/virtual/')
|
108
|
+
assert_equal nil, found
|
109
|
+
end
|
110
|
+
|
111
|
+
def render_page
|
112
|
+
lazy_initialize_parser_and_context
|
113
|
+
if layout = @page.layout
|
114
|
+
parse_object(layout)
|
115
|
+
else
|
116
|
+
render_page_part(:body)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def render_page_part(part_name)
|
121
|
+
lazy_initialize_parser_and_context
|
122
|
+
part = @page.part(part_name)
|
123
|
+
if part
|
124
|
+
parse_object(part)
|
125
|
+
else
|
126
|
+
''
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def render_snippet(snippet)
|
131
|
+
lazy_initialize_parser_and_context
|
132
|
+
parse_object(snippet)
|
133
|
+
end
|
134
|
+
|
135
|
+
def render_text(text)
|
136
|
+
lazy_initialize_parser_and_context
|
137
|
+
parse(text)
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_tags_to_child_context(behavior)
|
141
|
+
self.class.additional_child_tag_definition_blocks.each { |block| behavior.instance_eval &block }
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def lazy_initialize_parser_and_context
|
147
|
+
unless @context and @parser
|
148
|
+
@context = PageContext.new(@page)
|
149
|
+
self.class.additional_tag_definition_blocks.each { |block| instance_eval &block }
|
150
|
+
add_tags_from_parent_to_context
|
151
|
+
@parser = Radius::Parser.new(@context, :tag_prefix => 'r')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def clean_url(url)
|
156
|
+
"/#{ url.strip }/".gsub(%r{//+}, '/')
|
157
|
+
end
|
158
|
+
|
159
|
+
def parse_object(object)
|
160
|
+
text = object.content
|
161
|
+
text = parse(text)
|
162
|
+
text = object.filter.filter(text) if object.respond_to? :filter_id
|
163
|
+
text
|
164
|
+
end
|
165
|
+
|
166
|
+
def parse(text)
|
167
|
+
@parser.parse(text)
|
168
|
+
end
|
169
|
+
|
170
|
+
def parent
|
171
|
+
@page.parent
|
172
|
+
end
|
173
|
+
|
174
|
+
def parent?
|
175
|
+
not parent.nil?
|
176
|
+
end
|
177
|
+
|
178
|
+
def parent_behavior
|
179
|
+
parent.behavior if parent?
|
180
|
+
end
|
181
|
+
|
182
|
+
def parent_behavior?
|
183
|
+
not parent_behavior.nil?
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_tags_from_parent_to_context
|
187
|
+
parent_behavior.add_tags_to_child_context(self) if parent_behavior?
|
188
|
+
end
|
189
|
+
|
190
|
+
def tag(*args, &block)
|
191
|
+
@context.define_tag(*args, &block)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Layout < ActiveRecord::Base
|
2
|
+
|
3
|
+
# Associations
|
4
|
+
has_many :pages
|
5
|
+
belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by'
|
6
|
+
belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by'
|
7
|
+
|
8
|
+
# Validations
|
9
|
+
validates_presence_of :name, :message => 'required'
|
10
|
+
validates_uniqueness_of :name, :message => 'name already in use'
|
11
|
+
validates_length_of :name, :maximum => 100, :message => '%d-character limit'
|
12
|
+
|
13
|
+
end
|
data/app/models/page.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require_dependency 'advanced_delegation'
|
2
|
+
|
3
|
+
class Page < ActiveRecord::Base
|
4
|
+
|
5
|
+
class MissingRootPageError < StandardError
|
6
|
+
def initialize(message = 'Database missing root page'); super end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Callbacks
|
10
|
+
before_save :update_published_at, :update_virtual
|
11
|
+
|
12
|
+
# Associations
|
13
|
+
acts_as_tree :order => 'virtual DESC, title ASC'
|
14
|
+
has_many :parts, :class_name => 'PagePart', :dependent => :destroy
|
15
|
+
belongs_to :layout
|
16
|
+
belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by'
|
17
|
+
belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by'
|
18
|
+
|
19
|
+
# Validations
|
20
|
+
validates_presence_of :title, :slug, :breadcrumb, :status_id, :message => 'required'
|
21
|
+
|
22
|
+
validates_length_of :title, :maximum => 255, :message => '%d-character limit'
|
23
|
+
validates_length_of :slug, :maximum => 100, :message => '%d-character limit'
|
24
|
+
validates_length_of :breadcrumb, :maximum => 160, :message => '%d-character limit'
|
25
|
+
validates_length_of :behavior_id, :maximum => 25, :allow_nil => true, :message => '%d-character limit'
|
26
|
+
|
27
|
+
validates_format_of :slug, :with => %r{^([-_.A-Za-z0-9]*|/)$}, :message => 'invalid format'
|
28
|
+
validates_uniqueness_of :slug, :scope => :parent_id, :message => 'slug already in use for child of parent'
|
29
|
+
validates_numericality_of :id, :status_id, :parent_id, :allow_nil => true, :only_integer => true, :message => 'must be a number'
|
30
|
+
|
31
|
+
delegate_to :behavior, :url => :page_url, :cache? => :cache_page?, :find_by_url => :find_page_by_url,
|
32
|
+
:render => :render_page, :virtual? => :page_virtual?
|
33
|
+
|
34
|
+
delegate_to :behavior, :process, :child_url
|
35
|
+
|
36
|
+
def after_initialize
|
37
|
+
class << self
|
38
|
+
define_method(:layout) do |*params|
|
39
|
+
super || (parent and parent.layout)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def part(name)
|
45
|
+
parts.find_by_name name.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
Status.find(self.status_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def status=(value)
|
53
|
+
self.status_id = value.id
|
54
|
+
end
|
55
|
+
|
56
|
+
def published?
|
57
|
+
status == Status[:published]
|
58
|
+
end
|
59
|
+
|
60
|
+
def behavior
|
61
|
+
if @behavior.nil? or (@old_behavior_id != behavior_id)
|
62
|
+
@old_behavior_id = behavior_id
|
63
|
+
@behavior = Behavior[behavior_id].new(self)
|
64
|
+
else
|
65
|
+
@behavior
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.find_by_url(url, live = true)
|
70
|
+
root = find_by_parent_id(nil)
|
71
|
+
raise MissingRootPageError unless root
|
72
|
+
root.find_by_url(url, live)
|
73
|
+
end
|
74
|
+
|
75
|
+
def virtual
|
76
|
+
!(read_attribute('virtual').to_s =~ /^(false|f|0|)$/)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def update_published_at
|
82
|
+
write_attribute('published_at', Time.now) if (status_id.to_i == Status[:published].id) and published_at.nil?
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_virtual
|
87
|
+
write_attribute('virtual', virtual?)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,526 @@
|
|
1
|
+
class PageContext < Radius::Context
|
2
|
+
class TagError < StandardError; end
|
3
|
+
|
4
|
+
attr_reader :page
|
5
|
+
|
6
|
+
def initialize(page)
|
7
|
+
super()
|
8
|
+
|
9
|
+
@page = page
|
10
|
+
globals.page = @page
|
11
|
+
|
12
|
+
# <r:page>...</r:page>
|
13
|
+
#
|
14
|
+
# Causes the tags referring to a page's attributes to refer to the current page.
|
15
|
+
#
|
16
|
+
define_tag 'page' do |tag|
|
17
|
+
tag.locals.page = tag.globals.page
|
18
|
+
tag.expand
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# <r:url />
|
23
|
+
# <r:title />
|
24
|
+
# etc...
|
25
|
+
#
|
26
|
+
((@page.attributes.symbolize_keys.keys + [:url]) - [:created_by, :updated_by]).each do |method|
|
27
|
+
define_tag(method.to_s) do |tag|
|
28
|
+
tag.locals.page.send(method)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# <r:children>...</r:children>
|
34
|
+
#
|
35
|
+
# Gives access to a page's children.
|
36
|
+
#
|
37
|
+
define_tag 'children' do |tag|
|
38
|
+
tag.locals.children = tag.locals.page.children
|
39
|
+
tag.expand
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# <r:children:count />
|
44
|
+
#
|
45
|
+
# Renders the total number of children.
|
46
|
+
#
|
47
|
+
define_tag 'children:count' do |tag|
|
48
|
+
tag.locals.children.count
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# <r:children:first>...<r:children:first>
|
53
|
+
#
|
54
|
+
# Returns the first child. Inside this tag all page attribute tags are mapped to
|
55
|
+
# the first child.
|
56
|
+
#
|
57
|
+
define_tag 'children:first' do |tag|
|
58
|
+
children = tag.locals.children
|
59
|
+
if first = children.first
|
60
|
+
tag.locals.page = first
|
61
|
+
tag.expand
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# <r:children:last>...</r:children:last>
|
67
|
+
#
|
68
|
+
# Returns the last child. Inside this tag all page attribute tags are mapped to
|
69
|
+
# the last child.
|
70
|
+
#
|
71
|
+
define_tag 'children:last' do |tag|
|
72
|
+
children = tag.locals.children
|
73
|
+
if last = children.last
|
74
|
+
tag.locals.page = last
|
75
|
+
tag.expand
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# <r:children:each [offset="number"] [limit="number"] [by="attribute"] [order="asc|desc"]
|
81
|
+
# [status="draft|reviewed|published|hidden|all"]>
|
82
|
+
# ...
|
83
|
+
# </r:children:each>
|
84
|
+
#
|
85
|
+
# Cycles through each of the children. Inside this tag all page attribute tags
|
86
|
+
# are mapped to the current child page.
|
87
|
+
#
|
88
|
+
define_tag "children:each" do |tag|
|
89
|
+
attr = tag.attr.symbolize_keys
|
90
|
+
|
91
|
+
options = {}
|
92
|
+
|
93
|
+
[:limit, :offset].each do |symbol|
|
94
|
+
if number = attr[symbol]
|
95
|
+
if number =~ /^\d{1,4}$/
|
96
|
+
options[symbol] = number.to_i
|
97
|
+
else
|
98
|
+
raise TagError.new("`#{symbol}' attribute of `each' tag must be a positive number between 1 and 4 digits")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
by = (attr[:by] || 'published_at').strip
|
104
|
+
order = (attr[:order] || 'asc').strip
|
105
|
+
order_string = ''
|
106
|
+
if @page.attributes.keys.include?(by)
|
107
|
+
order_string << by
|
108
|
+
else
|
109
|
+
raise TagError.new("`by' attribute of `each' tag must be set to a valid field name")
|
110
|
+
end
|
111
|
+
if order =~ /^(asc|desc)$/i
|
112
|
+
order_string << " #{$1.upcase}"
|
113
|
+
else
|
114
|
+
raise TagError.new(%{`order' attribute of `each' tag must be set to either "asc" or "desc"})
|
115
|
+
end
|
116
|
+
options[:order] = order_string
|
117
|
+
|
118
|
+
status = (attr[:status] || 'published').downcase
|
119
|
+
unless status == 'all'
|
120
|
+
stat = Status[status]
|
121
|
+
unless stat.nil?
|
122
|
+
options[:conditions] = ["(virtual = ?) and (status_id = ?)", false, stat.id]
|
123
|
+
else
|
124
|
+
raise TagError.new(%{`status' attribute of `each' tag must be set to a valid status})
|
125
|
+
end
|
126
|
+
else
|
127
|
+
options[:conditions] = ["virtual = ?", false]
|
128
|
+
end
|
129
|
+
|
130
|
+
result = []
|
131
|
+
children = tag.locals.children
|
132
|
+
tag.locals.previous_headers = {}
|
133
|
+
children.find(:all, options).each do |item|
|
134
|
+
tag.locals.child = item
|
135
|
+
tag.locals.page = item
|
136
|
+
result << tag.expand
|
137
|
+
end
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# <r:child>...</r:child>
|
143
|
+
#
|
144
|
+
# Page attribute tags inside of this tag refer to the current child. Not needed in
|
145
|
+
# most cases.
|
146
|
+
#
|
147
|
+
define_tag 'children:each:child' do |tag|
|
148
|
+
tag.locals.page = tag.locals.child
|
149
|
+
tag.expand
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# <header [name="header_name"] [restart="name1[;name2;...]"]>...</r:header>
|
154
|
+
#
|
155
|
+
# Renders the tag contents only if the contents do not match the previous header. This
|
156
|
+
# is extremely useful for rendering date headers for a list of child pages.
|
157
|
+
#
|
158
|
+
# If you would like to use several header blocks you may use the 'name' attribute to
|
159
|
+
# name the header. When a header is named it will not restart until another header of
|
160
|
+
# the same name is different.
|
161
|
+
#
|
162
|
+
# Using the 'restart' attribute you can cause other named headers to restart when the
|
163
|
+
# present header changes. Simply specify the names of the other headers in a semicolon
|
164
|
+
# separated list.
|
165
|
+
#
|
166
|
+
define_tag 'children:each:header' do |tag|
|
167
|
+
previous_headers = tag.locals.previous_headers
|
168
|
+
name = tag.attr['name'] || :unnamed
|
169
|
+
restart = (tag.attr['restart'] || '').split(';')
|
170
|
+
header = tag.expand
|
171
|
+
unless header == previous_headers[name]
|
172
|
+
previous_headers[name] = header
|
173
|
+
unless restart.empty?
|
174
|
+
restart.each do |n|
|
175
|
+
previous_headers[n] = nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
header
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# <r:content [part="part_name"] [inherit="true|false"] [contextual="true|false"] />
|
184
|
+
#
|
185
|
+
# Renders the main content of a page. Use the 'part' attribute to select a specific
|
186
|
+
# page part. By default the 'part' attribute is set to body. Use the 'inherit'
|
187
|
+
# attribute to specify that if a page does not have a content part by that name that
|
188
|
+
# the tag should render the parent's content part. By default 'inherit' is set to
|
189
|
+
# 'false'. Use the 'contextual' attribute to force a part inherited from a parent
|
190
|
+
# part to be evaluated in the context of the child page. By default 'contextual'
|
191
|
+
# is set to false.
|
192
|
+
#
|
193
|
+
define_tag 'content' do |tag|
|
194
|
+
page = tag.locals.page
|
195
|
+
part_name = tag_part_name(tag)
|
196
|
+
boolean_attr = proc do |attribute_name, default|
|
197
|
+
attribute = (tag.attr[attribute_name] || default).to_s
|
198
|
+
raise TagError.new(%{`#{attribute_name}' attribute of `content' tag must be set to either "true" or "false"}) unless attribute =~ /true|false/i
|
199
|
+
(attribute.downcase == 'true') ? true : false
|
200
|
+
end
|
201
|
+
inherit = boolean_attr['inherit', false]
|
202
|
+
part_page = page
|
203
|
+
if inherit
|
204
|
+
while (part_page.part(part_name).nil? and (not part_page.parent.nil?)) do
|
205
|
+
part_page = part_page.parent
|
206
|
+
end
|
207
|
+
end
|
208
|
+
contextual = boolean_attr['contextual', true]
|
209
|
+
if inherit and contextual
|
210
|
+
part = part_page.part(part_name)
|
211
|
+
page.behavior.render_snippet(part)
|
212
|
+
else
|
213
|
+
part_page.behavior.render_page_part(part_name)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
#
|
218
|
+
# <r:if_content [part="part_name"]>...</r:if_content>
|
219
|
+
#
|
220
|
+
# Renders the containing elements only if the part exists on a page. By default the
|
221
|
+
# 'part' attribute is set 'body'.
|
222
|
+
#
|
223
|
+
define_tag 'if_content' do |tag|
|
224
|
+
page = tag.locals.page
|
225
|
+
part_name = tag_part_name(tag)
|
226
|
+
unless page.part(part_name).nil?
|
227
|
+
tag.expand
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# <r:unless_content [part="part_name"]>...</r:unless_content>
|
233
|
+
#
|
234
|
+
# The opposite of the 'if_content' tag.
|
235
|
+
#
|
236
|
+
define_tag 'unless_content' do |tag|
|
237
|
+
page = tag.locals.page
|
238
|
+
part_name = tag_part_name(tag)
|
239
|
+
if page.part(part_name).nil?
|
240
|
+
tag.expand
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
#
|
245
|
+
# <r:if_url matches="regexp" [ignore_case="true|false"]>...</if_url>
|
246
|
+
#
|
247
|
+
# Renders the containing elements only if the page's url matches the regular expression
|
248
|
+
# given in the 'matches' attribute. If the 'ìgnore_case' attribute is set to false, the
|
249
|
+
# match is case sensitive. By default, 'ignore_case' is set to true.
|
250
|
+
#
|
251
|
+
define_tag 'if_url' do |tag|
|
252
|
+
raise TagError.new("`if_url' tag must contain a `matches' attribute.") unless tag.attr.has_key?('matches')
|
253
|
+
regexp = build_regexp_for(tag, 'matches')
|
254
|
+
unless tag.locals.page.url.match(regexp).nil?
|
255
|
+
tag.expand
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# <r:unless_url matches="regexp" [ignore_case="true|false"]>...</unless_url>
|
261
|
+
#
|
262
|
+
# The opposite of the 'if_url' tag.
|
263
|
+
#
|
264
|
+
define_tag 'unless_url' do |tag|
|
265
|
+
raise TagError.new("`unless_url' tag must contain a `matches' attribute.") unless tag.attr.has_key?('matches')
|
266
|
+
regexp = build_regexp_for(tag, 'matches')
|
267
|
+
if tag.locals.page.url.match(regexp).nil?
|
268
|
+
tag.expand
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# <r:author />
|
274
|
+
#
|
275
|
+
# Renders the name of the Author of the current page.
|
276
|
+
#
|
277
|
+
define_tag 'author' do |tag|
|
278
|
+
page = tag.locals.page
|
279
|
+
if author = page.created_by
|
280
|
+
author.name
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# <r:date [format="format_string"] />
|
286
|
+
#
|
287
|
+
# Renders the date that a page was published (or in the event that it has
|
288
|
+
# not ben modified yet, the date that it was created). The format attribute
|
289
|
+
# uses the same formating codes used by the Ruby +strftime+ function. By
|
290
|
+
# default it's set to '%A, %B %d, %Y'.
|
291
|
+
#
|
292
|
+
define_tag 'date' do |tag|
|
293
|
+
page = tag.locals.page
|
294
|
+
format = tag_time_format(tag)
|
295
|
+
if date = page.published_at || page.created_at
|
296
|
+
date.strftime(format)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
#
|
301
|
+
# <r:link [anchor="name"] [other attributes...] />
|
302
|
+
# <r:link>...</r:link>
|
303
|
+
#
|
304
|
+
# Renders a link to the page. When used as a single tag it uses the page's title
|
305
|
+
# for the link name. When used as a double tag the part inbetween both tags will
|
306
|
+
# be used as the link text. The link tag passes all attributes over to the HTML
|
307
|
+
# 'a' tag. This is very useful for passing attributes like the 'class' attribute
|
308
|
+
# or 'id' attribute. If the 'anchor' attribute is passed to the tag it will
|
309
|
+
# append a pound sign ('#') followed by the value of the attribute to the 'href'
|
310
|
+
# attribute of the HTML 'a' tag--effectively making an HTML anchor.
|
311
|
+
#
|
312
|
+
define_tag 'link' do |tag|
|
313
|
+
options = tag.attr.dup
|
314
|
+
anchor = options['anchor'] ? "##{options.delete('anchor')}" : ''
|
315
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
316
|
+
attributes = " #{attributes}" unless attributes.empty?
|
317
|
+
text = tag.double? ? tag.expand : tag.render('title')
|
318
|
+
%{<a href="#{tag.render('url')}#{anchor}"#{attributes}>#{text}</a>}
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# <r:breadcrumbs [separator="separator_string"] />
|
323
|
+
#
|
324
|
+
# Renders a trail of breadcrumbs to the current page. The separator attribute
|
325
|
+
# specifies the HTML fragment that is inserted between each of the breadcrumbs. By
|
326
|
+
# default it is set to ' > '.
|
327
|
+
#
|
328
|
+
define_tag 'breadcrumbs' do |tag|
|
329
|
+
page = tag.locals.page
|
330
|
+
breadcrumbs = [page.breadcrumb]
|
331
|
+
page.ancestors.each do |ancestor|
|
332
|
+
breadcrumbs.unshift %{<a href="#{ancestor.url}">#{ancestor.breadcrumb}</a>}
|
333
|
+
end
|
334
|
+
separator = tag.attr['separator'] || ' > '
|
335
|
+
breadcrumbs.join(separator)
|
336
|
+
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# <r:snippet name="snippet_name" />
|
340
|
+
#
|
341
|
+
# Renders the snippet specified in the 'name' attribute within the context of a page.
|
342
|
+
#
|
343
|
+
define_tag 'snippet' do |tag|
|
344
|
+
if name = tag.attr['name']
|
345
|
+
if snippet = Snippet.find_by_name(name.strip)
|
346
|
+
page = tag.locals.page
|
347
|
+
page.behavior.render_snippet(snippet)
|
348
|
+
else
|
349
|
+
raise TagError.new('snippet not found')
|
350
|
+
end
|
351
|
+
else
|
352
|
+
raise TagError.new("`snippet' tag must contain `name' attribute")
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
#
|
357
|
+
# <r:find url="value_to_find">...</r:find>
|
358
|
+
#
|
359
|
+
# Inside this tag all page related tags refer to the tag found at the 'url' attribute.
|
360
|
+
#
|
361
|
+
define_tag 'find' do |tag|
|
362
|
+
if url = tag.attr['url']
|
363
|
+
if found = Page.find_by_url(tag.attr['url'])
|
364
|
+
tag.locals.page = found
|
365
|
+
tag.expand
|
366
|
+
end
|
367
|
+
else
|
368
|
+
raise TagError.new("`find' tag must contain `url' attribute")
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
#
|
373
|
+
# <r:random>
|
374
|
+
# <r:option>...</r:option>
|
375
|
+
# <r:option>...</r:option>
|
376
|
+
# ...
|
377
|
+
# <r:random>
|
378
|
+
#
|
379
|
+
# Randomly renders one of the options specified by the 'option' tags.
|
380
|
+
#
|
381
|
+
define_tag 'random' do |tag|
|
382
|
+
tag.locals.random = []
|
383
|
+
tag.expand
|
384
|
+
options = tag.locals.random
|
385
|
+
option = options[rand(options.size)]
|
386
|
+
option.call if option
|
387
|
+
end
|
388
|
+
define_tag 'random:option' do |tag|
|
389
|
+
items = tag.locals.random
|
390
|
+
items << tag.block
|
391
|
+
end
|
392
|
+
|
393
|
+
#
|
394
|
+
# <r:comment>...</r:comment>
|
395
|
+
#
|
396
|
+
# Nothing inside a set of comment tags is rendered.
|
397
|
+
#
|
398
|
+
define_tag 'comment' do |tag|
|
399
|
+
end
|
400
|
+
|
401
|
+
#
|
402
|
+
# <r:escape_html>...</r:escape_html>
|
403
|
+
#
|
404
|
+
# Escapes angle brackets, etc...
|
405
|
+
#
|
406
|
+
define_tag "escape_html" do |tag|
|
407
|
+
CGI.escapeHTML(tag.expand)
|
408
|
+
end
|
409
|
+
|
410
|
+
#
|
411
|
+
# <r:rfc1123_date />
|
412
|
+
#
|
413
|
+
# Outputs the date using the format mandated by RFC 1123. (Ideal for RSS feeds.)
|
414
|
+
#
|
415
|
+
define_tag "rfc1123_date" do |tag|
|
416
|
+
page = tag.locals.page
|
417
|
+
if date = page.published_at || page.created_at
|
418
|
+
CGI.rfc1123_date(date.to_time)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
#
|
423
|
+
# <r:navigation urls="[Title: url; Title: url; ...]">
|
424
|
+
# <r:normal><a href="<r:url />"><r:title /></a></r:normal>
|
425
|
+
# <r:here><strong><r:title /></strong></r:here>
|
426
|
+
# <r:selected><strong><a href="<r:url />"><r:title /></a></strong></r:selected>
|
427
|
+
# <r:between> | </r:between>
|
428
|
+
# </r:navigation>
|
429
|
+
#
|
430
|
+
# Renders a list of links specified in the 'urls' attribute according to three
|
431
|
+
# states:
|
432
|
+
#
|
433
|
+
# * 'normal' specifies the normal state for the link
|
434
|
+
# * 'here' specifies the state of the link when the url matches the current
|
435
|
+
# page's url
|
436
|
+
# * 'selected' specifies the state of the link when the current page matches
|
437
|
+
# is a child of the specified url
|
438
|
+
#
|
439
|
+
# The 'between' tag specifies what sould be inserted inbetween each of the links.
|
440
|
+
#
|
441
|
+
define_tag 'navigation' do |tag|
|
442
|
+
hash = tag.locals.navigation = {}
|
443
|
+
tag.expand
|
444
|
+
raise TagError.new("`navigation' tag must include a `normal' tag") unless hash.has_key? :normal
|
445
|
+
result = []
|
446
|
+
pairs = tag.attr['urls'].to_s.split(';').collect do |pair|
|
447
|
+
parts = pair.split(':')
|
448
|
+
value = parts.pop
|
449
|
+
key = parts.join(':')
|
450
|
+
[key.strip, value.strip]
|
451
|
+
end
|
452
|
+
pairs.each do |title, url|
|
453
|
+
compare_url = remove_trailing_slash(url)
|
454
|
+
page_url = remove_trailing_slash(@page.url)
|
455
|
+
hash[:title] = title
|
456
|
+
hash[:url] = url
|
457
|
+
case page_url
|
458
|
+
when compare_url
|
459
|
+
result << (hash[:here] || hash[:selected] || hash[:normal]).call
|
460
|
+
when Regexp.compile( '^' + Regexp.quote(url))
|
461
|
+
result << (hash[:selected] || hash[:normal]).call
|
462
|
+
else
|
463
|
+
result << hash[:normal].call
|
464
|
+
end
|
465
|
+
end
|
466
|
+
between = hash.has_key?(:between) ? hash[:between].call : ' '
|
467
|
+
result.join(between)
|
468
|
+
end
|
469
|
+
[:normal, :here, :selected, :between].each do |symbol|
|
470
|
+
define_tag "navigation:#{symbol}" do |tag|
|
471
|
+
hash = tag.locals.navigation
|
472
|
+
hash[symbol] = tag.block
|
473
|
+
end
|
474
|
+
end
|
475
|
+
[:title, :url].each do |symbol|
|
476
|
+
define_tag "navigation:#{symbol}" do |tag|
|
477
|
+
hash = tag.locals.navigation
|
478
|
+
hash[symbol]
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
|
484
|
+
def render_tag(name, attributes = {}, &block)
|
485
|
+
super
|
486
|
+
rescue Exception => e
|
487
|
+
render_error_message(e.message)
|
488
|
+
end
|
489
|
+
|
490
|
+
def tag_missing(name, attributes = {}, &block)
|
491
|
+
super
|
492
|
+
rescue Radius::UndefinedTagError => e
|
493
|
+
raise TagError.new(e.message)
|
494
|
+
end
|
495
|
+
|
496
|
+
private
|
497
|
+
|
498
|
+
def render_error_message(message)
|
499
|
+
"<div><strong>#{message}</strong></div>"
|
500
|
+
end
|
501
|
+
|
502
|
+
def remove_trailing_slash(string)
|
503
|
+
(string =~ %r{^(.*?)/$}) ? $1 : string
|
504
|
+
end
|
505
|
+
|
506
|
+
def tag_part_name(tag)
|
507
|
+
tag.attr['part'] || 'body'
|
508
|
+
end
|
509
|
+
|
510
|
+
def tag_time_format(tag)
|
511
|
+
(tag.attr['format'] || '%A, %B %d, %Y')
|
512
|
+
end
|
513
|
+
|
514
|
+
def postgres?
|
515
|
+
ActiveRecord::Base.connection.adapter_name =~ /postgres/i
|
516
|
+
end
|
517
|
+
def build_regexp_for(tag,attribute_name)
|
518
|
+
ignore_case = tag.attr.has_key?('ignore_case') && tag.attr['ignore_case']=='false' ? nil : true
|
519
|
+
begin
|
520
|
+
regexp = Regexp.new(tag.attr['matches'],ignore_case)
|
521
|
+
rescue RegexpError => e
|
522
|
+
raise TagError.new("Malformed regular expression in `#{attribute_name}' argument of `#{tag.name}' tag: #{e.message}")
|
523
|
+
end
|
524
|
+
return regexp
|
525
|
+
end
|
526
|
+
end
|