merb_comatose 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 YOUR NAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,4 @@
1
+ merb_comatose
2
+ =============
3
+
4
+ A plugin for the Merb framework that provides ...
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "merb_comatose"
8
+ GEM_VERSION = "0.0.2"
9
+ AUTHOR = "Jacques Crocker"
10
+ EMAIL = "merbjedi@gmail.com"
11
+ HOMEPAGE = "http://merbjedi.com/"
12
+ SUMMARY = "Partial Port of Comatose with focus on rendering from Comatose Pages (no admin yet)"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.rubyforge_project = 'merb'
16
+ s.name = GEM_NAME
17
+ s.version = GEM_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
21
+ s.summary = SUMMARY
22
+ s.description = s.summary
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+
27
+ # s.add_dependency('merb', '>= 1.0')
28
+
29
+ s.require_path = 'lib'
30
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
31
+ end
32
+
33
+ Rake::GemPackageTask.new(spec) do |pkg|
34
+ pkg.gem_spec = spec
35
+ end
36
+
37
+ desc "install the plugin as a gem"
38
+ task :install do
39
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
40
+ end
41
+
42
+ desc "Uninstall the gem"
43
+ task :uninstall do
44
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
45
+ end
46
+
47
+ desc "Create a gemspec file"
48
+ task :gemspec do
49
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
50
+ file.puts spec.to_ruby
51
+ end
52
+ end
data/TODO ADDED
@@ -0,0 +1,5 @@
1
+ TODO:
2
+ Fix LICENSE with your name
3
+ Fix Rakefile with your name and contact info
4
+ Add your code to lib/merb_comatose.rb
5
+ Add your Merb rake tasks to lib/merb_comatose/merbtasks.rb
@@ -0,0 +1,36 @@
1
+ class Class
2
+ def define_option(name, default=nil)
3
+ sym = name.to_sym
4
+ cattr_reader(sym)
5
+ cattr_writer(sym)
6
+ send("#{name.to_s}=", default)
7
+ end
8
+
9
+ def blockable_attr_accessor(sym)
10
+ module_eval(<<-EVAL, __FILE__, __LINE__)
11
+ def #{sym}(&block)
12
+ if block_given?
13
+ @#{sym} = block
14
+ else
15
+ @#{sym}
16
+ end
17
+ end
18
+ def #{sym}=(value)
19
+ @#{sym} = value
20
+ end
21
+ EVAL
22
+ end
23
+ end
24
+
25
+ class Module
26
+ def attr_accessor_with_default(sym, default = nil, &block)
27
+ raise 'Default value or block required' unless !default.nil? || block
28
+ define_method(sym, block_given? ? block : Proc.new { default })
29
+ module_eval(<<-EVAL, __FILE__, __LINE__)
30
+ def #{sym}=(value)
31
+ class << self; attr_reader :#{sym} end
32
+ @#{sym} = value
33
+ end
34
+ EVAL
35
+ end
36
+ end
@@ -0,0 +1,64 @@
1
+ module Comatose
2
+
3
+ class ComatoseDrop < ::Liquid::Drop
4
+
5
+ private :initialize
6
+
7
+ def before_method(method)
8
+ self.send(method.to_sym)
9
+ rescue
10
+ ComatoseController.logger.debug "Error calling #{method}: #{$!}"
11
+ raise $!
12
+ end
13
+
14
+ class << self
15
+ # Define a new ComatoseDrop by name
16
+ def define( name, &block )
17
+ d = ComatoseDrop.new
18
+ d.instance_eval(&block)
19
+ Comatose.registered_drops[name] = d
20
+ rescue
21
+ ComatoseController.logger.debug "Drop '#{name}' was not included: #{$!}"
22
+ end
23
+ end
24
+ end
25
+
26
+ class << self
27
+
28
+ # Returns/initializes a hash for storing ComatoseDrops
29
+ def registered_drops
30
+ @registered_drops ||= {}
31
+ end
32
+
33
+ # Simple wrapper around the ProcessingContext.define method
34
+ # I think Comatose.define_drop is probably simpler to remember too
35
+ def define_drop(name, &block)
36
+ ComatoseDrop.define(name, &block)
37
+ end
38
+
39
+ # Registers a 'filter' for Liquid use
40
+ def register_filter(module_name)
41
+ Liquid::Template.register_filter(module_name)
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+
48
+ #
49
+ # Some Default Filters/Drops
50
+ #
51
+
52
+ module IncludeFilter
53
+ def include(input)
54
+ page = ComatosePage.find_by_path(input)
55
+ params = @context['params']
56
+ # TODO: Add more of the context into the included page's context...
57
+ page.to_html( { 'params' => params } )
58
+ rescue
59
+ "Page at <tt>#{input}</tt> could not be found. <pre>#{$!}</pre>"
60
+ end
61
+ end
62
+
63
+ Comatose.register_filter IncludeFilter
64
+
@@ -0,0 +1,69 @@
1
+ module Comatose
2
+
3
+ def self.config
4
+ @@config ||= Configuration.new
5
+ end
6
+
7
+ def self.configure(&block)
8
+ raise "#configure must be sent a block" unless block_given?
9
+ yield config
10
+ config.validate!
11
+ end
12
+
13
+ # All of the 'mount' points for Comatose
14
+ def self.mount_points
15
+ @@mount_points ||= []
16
+ end
17
+
18
+ # Adds a 'mount' point to the list of known roots...
19
+ def self.add_mount_point(path, info={:index=>''})
20
+ path = "/#{path}" unless path.starts_with? '/'
21
+ info[:root]=path
22
+ mount_points << info
23
+ end
24
+
25
+ class Configuration
26
+
27
+ attr_accessor_with_default :admin_title, 'Comatose'
28
+ attr_accessor_with_default :admin_includes, []
29
+ attr_accessor_with_default :admin_helpers, []
30
+ attr_accessor_with_default :admin_sub_title, 'The Micro CMS'
31
+ attr_accessor_with_default :content_type, 'utf-8'
32
+ attr_accessor_with_default :default_filter, '[No Filter]'
33
+ attr_accessor_with_default :default_processor, :liquid
34
+ attr_accessor_with_default :default_tree_level, 2
35
+ attr_accessor_with_default :disable_caching, false
36
+ attr_accessor_with_default :hidden_meta_fields, []
37
+ attr_accessor_with_default :helpers, []
38
+ attr_accessor_with_default :includes, []
39
+ attr_accessor_with_default :allow_import_export, true
40
+
41
+ # 'Blockable' setters
42
+ blockable_attr_accessor :authorization
43
+ blockable_attr_accessor :admin_authorization
44
+ blockable_attr_accessor :admin_get_author
45
+ blockable_attr_accessor :admin_get_root_page
46
+ blockable_attr_accessor :after_setup
47
+
48
+ def initialize
49
+ # Default procs for blockable attrs....
50
+ @authorization = Proc.new { true }
51
+ @admin_authorization = Proc.new { true }
52
+ @admin_get_author = Proc.new { request.env['REMOTE_ADDR'] }
53
+ @admin_get_root_page = Proc.new { ComatosePage.root }
54
+ @after_setup = Proc.new { true }
55
+ end
56
+
57
+ def validate!
58
+ # Rips through the config and validates it's, er, valid
59
+ raise ConfigurationError.new( "admin_get_author must be a Proc or Symbol" ) unless @admin_get_author.is_a? Proc or @admin_get_author.is_a? Symbol
60
+ raise ConfigurationError.new( "admin_authorization must be a Proc or Symbol" ) unless @admin_authorization.is_a? Proc or @admin_authorization.is_a? Symbol
61
+ raise ConfigurationError.new( "authorization must be a Proc or Symbol" ) unless @authorization.is_a? Proc or @authorization.is_a? Symbol
62
+ true
63
+ end
64
+
65
+ class ConfigurationError < StandardError; end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,119 @@
1
+ # Giving direct access to the AR model is disconcerting, so this wraps the AR
2
+ # Model to prevent access to certain, destructive behavior
3
+ class Comatose::PageWrapper
4
+
5
+ @@allowed_methods = %w(id full_path uri slug keywords title to_html filter_type author updated_on created_on)
6
+ @@custom_methods = %w(link content parent next previous children rchildren first_child last_child has_keyword)
7
+
8
+ attr_accessor :page
9
+ private :page
10
+
11
+ def initialize( page, locals={} )
12
+ @page = page
13
+ @keyword_lst = []
14
+ @keyword_hsh = {}
15
+ unless page.nil?
16
+ @keyword_lst = (page.keywords || '').downcase.split(',').map {|k| k.strip }
17
+ @keyword_lst.each {|kw| @keyword_hsh[kw] = true}
18
+ end
19
+ @locals = locals
20
+ end
21
+
22
+ def has_keyword?(keyword)
23
+ @page.has_keyword?(keyword)
24
+ end
25
+
26
+ def has_keyword
27
+ @keyword_hsh
28
+ end
29
+
30
+ def next
31
+ @next_page ||= begin
32
+ if @page.lower_item.nil?
33
+ nil
34
+ else
35
+ Comatose::PageWrapper.new( @page.lower_item )
36
+ end
37
+ end
38
+ end
39
+
40
+ def previous
41
+ @prev_page ||= begin
42
+ if @page.higher_item.nil?
43
+ nil
44
+ else
45
+ Comatose::PageWrapper.new( @page.higher_item )
46
+ end
47
+ end
48
+ end
49
+
50
+ def first_child
51
+ children.first
52
+ end
53
+
54
+ def def last_child
55
+ children.last
56
+ end
57
+
58
+ # Generates a link to this page... You can pass in the link text,
59
+ # otherwise it will default to the page title.
60
+ def link( title=nil )
61
+ title = @page.title if title.nil?
62
+ TextFilters[@page.filter_type].create_link(title, @page.uri)
63
+ end
64
+
65
+ def content
66
+ @page.to_html( @locals )
67
+ end
68
+
69
+ def parent
70
+ @parent ||= begin
71
+ if @page.parent.nil?
72
+ nil
73
+ else
74
+ Comatose::PageWrapper.new( @page.parent )
75
+ end
76
+ end
77
+ end
78
+
79
+ def children
80
+ # Cache the results so that you can have multiple calls to #children without a penalty
81
+ @children ||= @page.children.to_a.collect {|child| Comatose::PageWrapper.new( child, @locals )}
82
+ end
83
+
84
+ # Children, in reverse order
85
+ def rchildren
86
+ children.reverse
87
+ end
88
+
89
+ def [](key)
90
+ if @@allowed_methods.include? key.to_s #or page.attributes.has_key? key.to_s
91
+ page.send( key )
92
+ elsif @@custom_methods.include? key
93
+ self.send(key.to_sym)
94
+ end
95
+ end
96
+
97
+ def has_key?(key)
98
+ @@allowed_methods.include? key or @@custom_methods.include? key
99
+ end
100
+
101
+ def to_s
102
+ @page.title
103
+ end
104
+
105
+ def to_liquid
106
+ self
107
+ end
108
+
109
+ def method_missing(method_id, *args)
110
+ #STDERR.puts "Looking for method: #{method_id} (#{method_id.class.to_s}) in [#{@@allowed_methods.join(', ')}] or [#{page.attributes.keys.join(', ')}]"
111
+ if @@allowed_methods.include? method_id.to_s or page.attributes.has_key? method_id.to_s
112
+ page.send( method_id, *args)
113
+ else
114
+ # Access nazi says: NO ACCESS FOR YOU!!
115
+ # but he says it silently...
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,69 @@
1
+ # This is the rendering context object you have access to in text processing...
2
+ class Comatose::ProcessingContext
3
+ @@supported_methods = %w(page include)
4
+
5
+ def initialize( page, locals={} )
6
+ @locals = locals.stringify_keys if locals.respond_to? :stringify_keys
7
+ @page = Comatose::PageWrapper.new(page, @locals)
8
+ end
9
+
10
+ def page
11
+ @page
12
+ end
13
+
14
+ def include(path, locals={})
15
+ begin
16
+ page = ComatosePage.find_by_path(path)
17
+ page.to_html( @locals.merge(locals) )
18
+ rescue
19
+ "<p>Page at <tt>#{path}</tt> could not be found.</p>"
20
+ end
21
+ end
22
+
23
+ def find_by_path(path)
24
+ begin
25
+ page = ComatosePage.find_by_path(path)
26
+ Comatose::PageWrapper.new(page, @locals)
27
+ rescue
28
+ "<p>Page at <tt>#{path}</tt> could not be found.</p>"
29
+ end
30
+ end
31
+
32
+ def [](key)
33
+ if key.to_s.downcase == 'page'
34
+ @page
35
+ elsif @locals.has_key? key
36
+ @locals[key]
37
+ elsif Comatose.registered_drops.has_key? key
38
+ Comatose.registered_drops[key]
39
+ end
40
+ end
41
+
42
+ def has_key?(key)
43
+ @@supported_methods.include?(key) or @locals.has_key?(key) or Comatose.registered_drops.has_key?(key)
44
+ end
45
+
46
+ def to_liquid
47
+ self
48
+ end
49
+
50
+ def get_binding
51
+ binding
52
+ end
53
+
54
+ def method_missing(method_id, *args)
55
+ method_name = method_id.to_s
56
+ if @locals.has_key? method_name
57
+ @locals[method_name]
58
+ elsif Comatose.registered_drops.has_key? method_name
59
+ Comatose.registered_drops[method_name].context = self
60
+ Comatose.registered_drops[method_name]
61
+ else
62
+ "<!-- ProcessingContext: Couldn't find #{method_id} -->"
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+
69
+
@@ -0,0 +1,4 @@
1
+ module Comatose
2
+ VERSION = "2.0.5"
3
+ VERSION_STRING = "#{VERSION} (beta)"
4
+ end
@@ -0,0 +1,96 @@
1
+ module Liquid
2
+
3
+ class Block < Tag
4
+ def parse(tokens)
5
+ @nodelist ||= []
6
+ @nodelist.clear
7
+
8
+ while token = tokens.shift
9
+
10
+ case token
11
+ when /^#{TagStart}/
12
+ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
13
+
14
+ # if we found the proper block delimitor just end parsing here and let the outer block
15
+ # proceed
16
+ if block_delimiter == $1
17
+ end_tag
18
+ return
19
+ end
20
+
21
+ # fetch the tag from registered blocks
22
+ if tag = Template.tags[$1]
23
+ @nodelist << tag.new($2, tokens)
24
+ else
25
+ # this tag is not registered with the system
26
+ # pass it to the current block for special handling or error reporting
27
+ unknown_tag($1, $2, tokens)
28
+ end
29
+ else
30
+ raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
31
+ end
32
+ when /^#{VariableStart}/
33
+ @nodelist << create_variable(token)
34
+ when ''
35
+ # pass
36
+ else
37
+ @nodelist << token
38
+ end
39
+ end
40
+
41
+ # Make sure that its ok to end parsing in the current block.
42
+ # Effectively this method will throw and exception unless the current block is
43
+ # of type Document
44
+ assert_missing_delimitation!
45
+ end
46
+
47
+ def end_tag
48
+ end
49
+
50
+ def unknown_tag(tag, params, tokens)
51
+ case tag
52
+ when 'else'
53
+ raise SyntaxError, "#{block_name} tag does not expect else tag"
54
+ when 'end'
55
+ raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
56
+ else
57
+ raise SyntaxError, "Unknown tag '#{tag}'"
58
+ end
59
+ end
60
+
61
+ def block_delimiter
62
+ "end#{block_name}"
63
+ end
64
+
65
+ def block_name
66
+ self.class.name.scan(/\w+$/).first.downcase
67
+ end
68
+
69
+ def create_variable(token)
70
+ token.scan(/^#{VariableStart}(.*)#{VariableEnd}$/) do |content|
71
+ return Variable.new(content.first)
72
+ end
73
+ raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
74
+ end
75
+
76
+ def render(context)
77
+ render_all(@nodelist, context)
78
+ end
79
+
80
+ protected
81
+
82
+ def assert_missing_delimitation!
83
+ raise SyntaxError.new("#{block_name} tag was never closed")
84
+ end
85
+
86
+ def render_all(list, context)
87
+ list.collect do |token|
88
+ if token.respond_to?(:render)
89
+ token.render(context)
90
+ else
91
+ token.to_s
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end