Changesets can be listed by changeset number.
The Git repository is here.
Changeset 37
Completed the ERB behavior code. Extended the system to allow
filters access to instance variables too, thus providing better
ERB filter behavior. ERB filter renamed to reflect the new
requirement for Radiant changes - the old one can be used side
by side if need be, though the older one is deleted here since
the Web site doesn't need it. Added "execute command on change"
for filters and added a command to clear caches and restart the
ROOL site server.
- Comitted by: adh
- Date: Saturday July 29 22:15:05 2006 (over 18 years ago)
Affected files:
- rool/rails/radiant/trunk/app/filters/erb_vars_filter.rb
- rool/rails/radiant/trunk/command
- rool/rails/radiant/trunk/app/filters/erb_filter.rb
- rool/rails/radiant/trunk/app/behaviors/erb_behavior.rb (diff)
- rool/rails/radiant/trunk/app/controllers/admin/snippet_controller.rb (diff)
- rool/rails/radiant/trunk/app/filters/textile_filter.rb (diff)
- rool/rails/radiant/trunk/app/models/behavior.rb (diff)
- rool/rails/radiant/trunk/app/models/page.rb (diff)
- rool/rails/radiant/trunk/app/models/snippet.rb (diff)
- rool/rails/radiant/trunk/app/views/admin/snippet/new.rhtml (diff)
- rool/rails/radiant/trunk/db/development_structure.sql (diff)
- rool/rails/radiant/trunk/db/migrate/001_create_radiant_tables.rb (diff)
- rool/rails/radiant/trunk/db/schema.rb (diff)
rool/rails/radiant/trunk/app/behaviors/erb_behavior.rb:
prev. | current | |
require 'erb' | ||
class ErbBehavior < Behavior::Base | ||
17 | | |
17 | register 'ERB' | |
description %{ | ||
Pages using this behavior can include ERB (embedded Ruby) in a manner | ||
... | ... | |
} | ||
# Radiant pages using the ERB behavior expect something close to | ||
39 | | |
39 | # ActionView behavior, so include ERB::Util for things like url_encode(). | |
include ERB::Util | ||
... | ... | |
# use to evaluate a Ruby source code equivalent of the ERB template that is | ||
# generated by the ERB module, munged to include our instance variables by | ||
# wrapping the ERB-generated code in an outer function declaration with | ||
47 | | |
47 | # various local variable definitions. | |
module ERBBehaviorCompiledTemplates | ||
end | ||
include ERBBehaviorCompiledTemplates | ||
54 | | |
54 | # Override the default rendering method. | |
def render_page | ||
lazy_initialize_parser_and_context() | ||
59 | | |
59 | # Ensure selected instance variables are stored. | |
61 | | |
61 | vars = collect_instance_variables() | |
# Use the same parser routine that the superclass uses; if we have | ||
# a layout, parse it; if we have no layout, parse the page's 'body' | ||
65 | | |
65 | # part; capture the output. | |
if layout = @page.layout | ||
output = parse_object(layout) | ||
... | ... | |
end | ||
# Get a new ERB instance and use an undocumented method, as used by | ||
79 | | |
79 | # ActionView, to get at the Ruby source equivalent of the template. | |
src = ERB.new(output).src | ||
# We now add code to set the instance variables inside that source. | ||
# To do this, we wrap the source in a function that takes the hash of | ||
# variables then self-initialises based on that hash; this is straight | ||
86 | | |
86 | # out of ActionView too, pretty much. | |
88 | | |
89 | | |
88 | locals_code = '' | |
89 | locals_keys = vars.keys | |
locals_keys.each do |key| | ||
locals_code << "#{key} = local_variables[:#{key}] if local_variables.has_key?(:#{key})\n" | ||
end | ||
... | ... | |
"end\n" | ||
# Evaluate the code within the temporary internal class, thus defining | ||
100 | | |
100 | # the temporary rendering method. | |
ERBBehaviorCompiledTemplates.module_eval(code) | ||
104 | | |
104 | # Call that method to return the final result. | |
106 | | |
106 | return erbbehavior_render(vars) | |
end | ||
109 | | |
109 | # We can't cache pages that might change on every fetch. | |
def cache_page? | ||
false | ||
end | ||
114 | ||
115 | | |
116 | | |
117 | ||
118 | | |
119 | | |
120 | | |
121 | ||
122 | | |
123 | | |
124 | | |
125 | | |
126 | | |
end |
rool/rails/radiant/trunk/app/controllers/admin/snippet_controller.rb:
prev. | current | |
model :snippet | ||
def save | ||
8 | ||
9 | # Save the snippet by asking the superclass to do it before trying | |
10 | # to export the snippet or execute any related commands. | |
11 | ||
12 | result = super | |
13 | ||
14 | # Automatically export the snippet to the filesystem if required. | |
15 | ||
if (@snippet && @snippet.auto_export && @snippet.auto_export != '') | ||
f = File.new(@snippet.auto_export, "wb") | ||
raise "Error writing snippet to '#{@snippet.auto_export}'" if (f.write(@snippet.content) != @snippet.content.length) | ||
19 | f.close | |
end | ||
12 | | |
21 | ||
22 | # Run a command if one is specified. Do this in an independent thread | |
23 | # so the command can take a while; it might want to wait a bit then | |
24 | # restart the Web server, for example. | |
25 | ||
26 | if (@snippet && @snippet.change_exec && @snippet.change_exec != '') | |
27 | exec("#{@snippet.change_exec}") if fork.nil? | |
28 | end | |
29 | ||
30 | # Return the result of the original save attempt. | |
31 | ||
32 | return result | |
33 | ||
end | ||
end | ||
rool/rails/radiant/trunk/app/filters/textile_filter.rb:
prev. | current | |
end | ||
def self.help | ||
11 | | |
11 | return <<END_OF_HELP | |
12 | <a href="http://hobix.com/textile/quick.html" target="_blank" onClick="textile_filter_quickRedReference(); return false;">Texttile</a> | |
13 | ||
14 | <script language="JavaScript"> | |
15 | function textile_filter_quickRedReference() { | |
16 | window.open( | |
17 | "http://hobix.com/textile/quick.html", | |
18 | "redRef", | |
19 | "height=600,width=550,channelmode=0,dependent=0," + | |
20 | "directories=0,fullscreen=0,location=0,menubar=0," + | |
21 | "resizable=0,scrollbars=1,status=1,toolbar=0" | |
22 | ); | |
23 | } | |
24 | </script> | |
25 | END_OF_HELP | |
end | ||
end | ||
rool/rails/radiant/trunk/app/models/behavior.rb:
prev. | current | |
module Behavior | ||
include Registerable | ||
5 | | |
5 | ||
class Base | ||
7 | | |
7 | ||
include InheritableClassAttributes | ||
9 | | |
9 | ||
cattr_inheritable_reader :additional_tag_definition_blocks | ||
@additional_tag_definition_blocks = [] | ||
12 | | |
12 | ||
cattr_inheritable_reader :additional_child_tag_definition_blocks | ||
@additional_child_tag_definition_blocks = [] | ||
15 | | |
15 | ||
cattr_inheritable_accessor :description | ||
17 | | |
17 | ||
class << self | ||
def description(value = nil) | ||
if value.nil? | ||
... | ... | |
@description = value | ||
end | ||
end | ||
26 | | |
26 | ||
def define_tags(&block) | ||
@additional_tag_definition_blocks << block | ||
end | ||
30 | | |
30 | ||
def define_child_tags(&block) | ||
@additional_child_tag_definition_blocks << block | ||
end | ||
end | ||
35 | | |
35 | ||
attr_accessor :page, :request, :response | ||
37 | | |
38 | | |
37 | ||
38 | def initialize(page, controller = nil) | |
@page = page | ||
40 | @controller = controller | |
end | ||
41 | | |
42 | ||
def description | ||
self.class.description | ||
end | ||
45 | | |
46 | ||
def process(request, response) | ||
@request, @response = request, response | ||
if @page.layout | ||
... | ... | |
@response.body = render_page | ||
@request, @response = nil, nil | ||
end | ||
56 | | |
57 | ||
def page_url | ||
if parent_behavior? | ||
clean_url(parent_behavior.child_url(@page)) | ||
... | ... | |
clean_url(@page.slug) | ||
end | ||
end | ||
64 | | |
65 | ||
def child_url(child) | ||
clean_url(page_url + '/' + child.slug) | ||
end | ||
68 | | |
69 | ||
def page_headers | ||
{ 'Status' => ActionController::Base::DEFAULT_RENDER_STATUS_CODE } | ||
end | ||
72 | | |
73 | ||
def page_virtual? | ||
false | ||
end | ||
76 | | |
77 | ||
def page_config | ||
string = render_page_part(:config) | ||
unless string.empty? | ||
... | ... | |
{} | ||
end | ||
end | ||
85 | | |
86 | ||
def cache_page? | ||
true | ||
end | ||
89 | | |
90 | ||
def find_page_by_url(url, live = true, clean = true) | ||
url = clean_url(url) if clean | ||
if page_url == url && (not live or @page.published?) | ||
... | ... | |
found = @behavior.find_page_by_url('/parent/virtual/') | ||
assert_equal nil, found | ||
end | ||
110 | | |
111 | ||
def render_page | ||
lazy_initialize_parser_and_context | ||
if layout = @page.layout | ||
... | ... | |
lazy_initialize_parser_and_context | ||
parse_object(snippet) | ||
end | ||
134 | | |
135 | ||
def render_text(text) | ||
lazy_initialize_parser_and_context | ||
parse(text) | ||
end | ||
139 | | |
140 | ||
def add_tags_to_child_context(behavior) | ||
self.class.additional_child_tag_definition_blocks.each { |block| behavior.instance_eval &block } | ||
end | ||
143 | | |
144 | ||
private | ||
145 | | |
146 | ||
def lazy_initialize_parser_and_context | ||
unless @context and @parser | ||
@context = PageContext.new(@page) | ||
... | ... | |
@parser = Radius::Parser.new(@context, :tag_prefix => 'r') | ||
end | ||
end | ||
154 | | |
155 | ||
def clean_url(url) | ||
156 | | |
157 | "/#{ url.strip }/".gsub(%r{//+}, '/') | |
end | ||
def parse_object(object) | ||
160 | | |
161 | text = object.content() | |
text = parse(text) | ||
162 | | |
163 | | |
163 | ||
164 | # Call a filter if a filter is set for the object. The extended filter | |
165 | # interface collects instance variables from this model into a hash and | |
166 | # passes this to the filter code. | |
167 | ||
168 | if (object.respond_to?(:filter_id)) | |
169 | if (object.filter.respond_to?(:extended_filter)) | |
170 | vars = collect_instance_variables() | |
171 | text = object.filter.extended_filter(text, vars) | |
172 | else | |
173 | text = object.filter.filter(text) | |
174 | end | |
175 | end | |
176 | ||
177 | return text | |
end | ||
165 | | |
179 | ||
def parse(text) | ||
@parser.parse(text) | ||
end | ||
169 | | |
183 | ||
def parent | ||
@page.parent | ||
end | ||
173 | | |
187 | ||
def parent? | ||
not parent.nil? | ||
end | ||
177 | | |
191 | ||
def parent_behavior | ||
parent.behavior if parent? | ||
end | ||
181 | | |
195 | ||
def parent_behavior? | ||
not parent_behavior.nil? | ||
end | ||
185 | | |
199 | ||
def add_tags_from_parent_to_context | ||
parent_behavior.add_tags_to_child_context(self) if parent_behavior? | ||
end | ||
189 | | |
203 | ||
def tag(*args, &block) | ||
@context.define_tag(*args, &block) | ||
end | ||
207 | ||
208 | # Add all of our instance variables to a hash, except for any | |
209 | # items from a protected list. | |
210 | ||
211 | def collect_instance_variables | |
212 | variables = {} | |
213 | @@protected_variables = [ '@variables' ] | |
214 | ||
215 | instance_variables.each do |var| | |
216 | next if @@protected_variables.include?(var) | |
217 | variables[var[1..-1]] = instance_variable_get(var) | |
218 | end | |
219 | ||
220 | return variables | |
221 | end | |
222 | ||
end | ||
194 | ||
225 | end |
rool/rails/radiant/trunk/app/models/page.rb:
prev. | current | |
require_dependency 'advanced_delegation' | ||
class Page < ActiveRecord::Base | ||
4 | | |
4 | ||
class MissingRootPageError < StandardError | ||
def initialize(message = 'Database missing root page'); super end | ||
end | ||
8 | | |
8 | ||
# Callbacks | ||
before_save :update_published_at, :update_virtual | ||
11 | | |
11 | ||
# Associations | ||
acts_as_tree :order => 'virtual DESC, title ASC' | ||
has_many :parts, :class_name => 'PagePart', :dependent => :destroy | ||
belongs_to :layout | ||
belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by' | ||
belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by' | ||
18 | | |
18 | ||
# Validations | ||
validates_presence_of :title, :slug, :breadcrumb, :status_id, :message => 'required' | ||
21 | | |
21 | ||
validates_length_of :title, :maximum => 255, :message => '%d-character limit' | ||
validates_length_of :slug, :maximum => 100, :message => '%d-character limit' | ||
validates_length_of :breadcrumb, :maximum => 160, :message => '%d-character limit' | ||
validates_length_of :behavior_id, :maximum => 25, :allow_nil => true, :message => '%d-character limit' | ||
26 | | |
27 | | |
26 | ||
27 | validates_format_of :slug, :with => %r{^([-_.A-Za-z0-9]*|/)$}, :message => 'invalid format' | |
validates_uniqueness_of :slug, :scope => :parent_id, :message => 'slug already in use for child of parent' | ||
validates_numericality_of :id, :status_id, :parent_id, :allow_nil => true, :only_integer => true, :message => 'must be a number' | ||
30 | | |
30 | ||
delegate_to :behavior, :url => :page_url, :cache? => :cache_page?, :find_by_url => :find_page_by_url, | ||
:render => :render_page, :virtual? => :page_virtual? | ||
33 | | |
33 | ||
delegate_to :behavior, :process, :child_url | ||
35 | | |
35 | ||
def after_initialize | ||
class << self | ||
define_method(:layout) do |*params| | ||
... | ... | |
end | ||
end | ||
end | ||
43 | | |
43 | ||
def part(name) | ||
parts.find_by_name name.to_s | ||
end | ||
47 | | |
47 | ||
def status | ||
Status.find(self.status_id) | ||
end | ||
51 | | |
51 | ||
def status=(value) | ||
self.status_id = value.id | ||
end | ||
55 | | |
55 | ||
def published? | ||
status == Status[:published] | ||
end | ||
59 | | |
59 | ||
def behavior | ||
if @behavior.nil? or (@old_behavior_id != behavior_id) | ||
@old_behavior_id = behavior_id | ||
... | ... | |
@behavior | ||
end | ||
end | ||
68 | | |
68 | ||
def self.find_by_url(url, live = true) | ||
root = find_by_parent_id(nil) | ||
raise MissingRootPageError unless root | ||
root.find_by_url(url, live) | ||
end | ||
74 | | |
74 | ||
def virtual | ||
!(read_attribute('virtual').to_s =~ /^(false|f|0|)$/) | ||
end | ||
78 | | |
78 | ||
private | ||
80 | | |
80 | ||
def update_published_at | ||
write_attribute('published_at', Time.now) if (status_id.to_i == Status[:published].id) and published_at.nil? | ||
true | ||
end | ||
85 | | |
85 | ||
def update_virtual | ||
write_attribute('virtual', virtual?) | ||
true | ||
end | ||
90 | | |
90 | ||
end | ||
rool/rails/radiant/trunk/app/models/snippet.rb:
prev. | current | |
validates_length_of :name, :maximum => 100, :message => '%d-character limit' | ||
validates_length_of :filter_id, :maximum => 25, :allow_nil => true, :message => '%d-character limit' | ||
validates_length_of :auto_export, :maximum => 512, :allow_nil => true, :message => '%d-character limit' | ||
12 | validates_length_of :change_exec, :maximum => 512, :allow_nil => true, :message => '%d-character limit' | |
validates_format_of :name, :with => %r{^\S*$}, :message => 'cannot contain spaces or tabs' | ||
validates_uniqueness_of :name, :message => 'name already in use' | ||
rool/rails/radiant/trunk/app/views/admin/snippet/new.rhtml:
prev. | current | |
<td><label for="snippet_auto_export">Export to file when altered</label></td> | ||
<td class="field"><%= text_field "snippet", "auto_export", :class => 'textbox', :maxlength => 512 %></td> | ||
</tr> | ||
19 | <tr> | |
20 | <td><label for="snippet_change_exec">Command to run when altered</label></td> | |
21 | <td class="field"><%= text_field "snippet", "change_exec", :class => 'textbox', :maxlength => 512 %></td> | |
22 | </tr> | |
</table> | ||
</div> | ||
<p> |
rool/rails/radiant/trunk/db/development_structure.sql:
prev. | current | |
`updated_at` datetime default NULL, | ||
`created_by` int(11) default NULL, | ||
`updated_by` int(11) default NULL, | ||
60 | `auto_export` varchar(512) default NULL, | |
61 | `change_exec` varchar(512) default NULL, | |
PRIMARY KEY (`id`), | ||
UNIQUE KEY `name` (`name`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=latin1; | ||
... | ... | |
UNIQUE KEY `login` (`login`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=latin1; | ||
80 | ||
83 | INSERT INTO schema_info (version) VALUES (8) |
rool/rails/radiant/trunk/db/migrate/001_create_radiant_tables.rb:
prev. | current | |
t.column "updated_at", :datetime | ||
t.column "created_by", :integer | ||
t.column "updated_by", :integer | ||
38 | t.column "auto_epoxrt", :string, :limit => 512 | |
39 | t.column "change_exec", :string, :limit => 512 | |
end | ||
add_index "snippets", ["name"], :name => "name", :unique => true | ||
rool/rails/radiant/trunk/db/schema.rb:
prev. | current | |
t.column "updated_at", :datetime | ||
t.column "created_by", :integer | ||
t.column "updated_by", :integer | ||
55 | t.column "auto_export", :string, :limit => 512 | |
56 | t.column "change_exec", :string, :limit => 512 | |
end | ||
add_index "snippets", ["name"], :name => "name", :unique => true |