rayo 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +154 -0
- data/lib/rayo/application.rb +22 -16
- data/lib/rayo/config.rb +24 -0
- data/lib/rayo/config/domain.rb +27 -0
- data/lib/rayo/models/page.rb +27 -10
- data/lib/rayo/models/renderable.rb +4 -2
- data/lib/rayo/models/root_page.rb +3 -3
- data/lib/rayo/models/status_page.rb +3 -3
- data/lib/rayo/storage.rb +72 -39
- data/lib/rayo/tag_context.rb +17 -0
- data/lib/rayo/tags/content_tags.rb +37 -26
- data/lib/rayo/tags/navigation_tags.rb +1 -1
- data/lib/rayo/tags/property_tags.rb +35 -7
- data/lib/rayo/version.rb +1 -1
- metadata +47 -9
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Alexey Noskov
|
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.rdoc
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
= Rayo
|
2
|
+
|
3
|
+
Rayo is a CMS based on lightweight {Sinatra framework}[http://www.sinatrarb.com/] and {Radius gem}[http://radius.rubyforge.org/]. It was inspired by {Radiant}[http://radiantcms.org/], very powerful ROR-based CMS.
|
4
|
+
|
5
|
+
<b>Caution! The project in a very early stage of development!</b>
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
Install rayo gem (recommended):
|
10
|
+
|
11
|
+
sudo gem install irwi
|
12
|
+
|
13
|
+
Or clone it:
|
14
|
+
|
15
|
+
git clone git://github.com/alno/rayo
|
16
|
+
|
17
|
+
== Rack application
|
18
|
+
|
19
|
+
To create basic Rayo-based application all you need is Rackup file (config.ru) with content similar to:
|
20
|
+
|
21
|
+
require '../rayo/lib/rayo.rb'
|
22
|
+
|
23
|
+
class Application < Rayo::Application
|
24
|
+
|
25
|
+
configure do |c|
|
26
|
+
c.content_dir = File.join( File.dirname(__FILE__), 'content' ) # Content location
|
27
|
+
c.languages = ['en','ru'] # Supported languages
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
run Application
|
33
|
+
|
34
|
+
== Content structure
|
35
|
+
|
36
|
+
Here we create our application by extending Rayo::Application class, set content directory location and supported languages.
|
37
|
+
Content should be placed in <tt>content</tt> subdirectories similar to following scheme:
|
38
|
+
|
39
|
+
content
|
40
|
+
+ layouts
|
41
|
+
+ base.html
|
42
|
+
+ snippets
|
43
|
+
+ footer.html
|
44
|
+
+ pages
|
45
|
+
+ index.yml
|
46
|
+
+ index.html
|
47
|
+
+ index.sidebar.ru.html
|
48
|
+
+ index.sidebar.en.html
|
49
|
+
+ section.yml
|
50
|
+
+ section.html
|
51
|
+
+ section
|
52
|
+
+ subpage.yml
|
53
|
+
+ subpage.html
|
54
|
+
|
55
|
+
There are example layout, snippet and 3 pages: <tt>index</tt>, <tt>section</tt> and <tt>section/subpage</tt>. Index page contains additional page part <tt>sidebar</tt>, wchih is translated to English and Russian.
|
56
|
+
It should be noticed what each page have corresponding .yml file. This file contains page properties which should be used in page rendering.
|
57
|
+
|
58
|
+
=== Generic pages
|
59
|
+
|
60
|
+
You may create generic pages which are rendered for a set of different paths. For example to create archive pages in blog you may use:
|
61
|
+
|
62
|
+
pages
|
63
|
+
+ 2010
|
64
|
+
| + 09
|
65
|
+
| + my-first-post.yml
|
66
|
+
| + my-first-post.html
|
67
|
+
+ %year.yml
|
68
|
+
+ %year.html
|
69
|
+
+ %year
|
70
|
+
+ %month.yml
|
71
|
+
+ %month.html
|
72
|
+
|
73
|
+
== Content formatting
|
74
|
+
|
75
|
+
=== Built-In Tags
|
76
|
+
|
77
|
+
=== Filters
|
78
|
+
|
79
|
+
== Configuration
|
80
|
+
|
81
|
+
Configuration options are provided in <tt>configure</tt> block in application class:
|
82
|
+
|
83
|
+
configure do |c|
|
84
|
+
c.content_dir = File.join( File.dirname(__FILE__), 'content' ) # Content location
|
85
|
+
c.languages = ['en','ru'] # Supported languages
|
86
|
+
end
|
87
|
+
|
88
|
+
Besides of these options you append your filters for processing content (after expanding radius tags):
|
89
|
+
|
90
|
+
c.add_filter 'textile' do |source|
|
91
|
+
RedCloth.new( source ).to_html
|
92
|
+
end
|
93
|
+
|
94
|
+
Also, you may define modules which contain tag definitions:
|
95
|
+
|
96
|
+
module MyTags
|
97
|
+
|
98
|
+
include Rayo::Taggable
|
99
|
+
|
100
|
+
tag 'hello' do
|
101
|
+
'Hello world'
|
102
|
+
end
|
103
|
+
|
104
|
+
tag 'repeat' do |tag|
|
105
|
+
number = (tag.attr['times'] || '1').to_i
|
106
|
+
result = ''
|
107
|
+
number.times { result << tag.expand }
|
108
|
+
result
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
And register it:
|
114
|
+
|
115
|
+
c.add_tags MyTags
|
116
|
+
|
117
|
+
=== Multidomain applications
|
118
|
+
|
119
|
+
Sometimes you may want to serve different domains with one application sharing layouts and snippets. Rayo has built-in support for this scenario. All you need is to create subdirectories in pages for different domains and add some lines to <tt>configure</tt> block:
|
120
|
+
|
121
|
+
To serve 'first.example.com' with pages from <tt>content/pages/first.example.com</tt> directory:
|
122
|
+
|
123
|
+
c.add_domain 'first.example.com'
|
124
|
+
|
125
|
+
To serve 'second.example.com' or 'www.second.example.com' with pages from <tt>content/pages/second</tt> directory:
|
126
|
+
|
127
|
+
c.add_domain 'second.', /^(www\.)?second\.example\.com$/
|
128
|
+
|
129
|
+
With both lines corresponding directory structure should be:
|
130
|
+
|
131
|
+
content
|
132
|
+
+ layouts
|
133
|
+
+ snippets
|
134
|
+
+ pages
|
135
|
+
+ first.example.com
|
136
|
+
+ second
|
137
|
+
|
138
|
+
That's all you need to build multi-domain application.
|
139
|
+
|
140
|
+
== Planned features
|
141
|
+
|
142
|
+
* Support for sites without I18n
|
143
|
+
* Support for different page formats
|
144
|
+
* Drop-in tag libary support
|
145
|
+
* Support for pages without .yml
|
146
|
+
* Generator which creates a set of static pages from content structure
|
147
|
+
|
148
|
+
== Contributors
|
149
|
+
|
150
|
+
* Alexey Noskov (http://github.com/alno)
|
151
|
+
|
152
|
+
Feel free to add yourself when you add new features.
|
153
|
+
|
154
|
+
Copyright (c) 2010 Alexey Noskov, released under the MIT license
|
data/lib/rayo/application.rb
CHANGED
@@ -7,7 +7,7 @@ class Rayo::Application < Sinatra::Base
|
|
7
7
|
|
8
8
|
class << self
|
9
9
|
|
10
|
-
attr_accessor :config
|
10
|
+
attr_accessor :config
|
11
11
|
|
12
12
|
# Configure application
|
13
13
|
def configure
|
@@ -20,35 +20,41 @@ class Rayo::Application < Sinatra::Base
|
|
20
20
|
self.class.config
|
21
21
|
end
|
22
22
|
|
23
|
-
get '
|
24
|
-
|
25
|
-
end
|
23
|
+
get '/*' do |path|
|
24
|
+
cfg = config.domain( request.host ) # Config for current host
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
26
|
+
if cfg
|
27
|
+
path = path.split '/' # Split path into segments
|
30
28
|
|
31
|
-
|
32
|
-
path = path.split '/' # Split path into segments
|
29
|
+
empty_segments_found = path.reject! {|e| e.empty? } # Clear path and detect empty segments
|
33
30
|
|
34
|
-
|
31
|
+
return redirect_to_lang path unless config.languages.include? path.first
|
32
|
+
return redirect '/' + path.join('/') if empty_segments_found
|
35
33
|
|
36
|
-
|
37
|
-
storage = Rayo::Storage.new( config, lang ) # Page storage
|
38
|
-
page = storage.page( path ) # Find page by path
|
39
|
-
page = storage.status_page( path, 404 ) unless page && page.file # Render 404 page if there are no page, or there are no file
|
34
|
+
lang = path.shift # Determine language
|
40
35
|
|
41
|
-
|
36
|
+
storage = create_storage( cfg ) # Page storage
|
37
|
+
page = storage.page( lang, path ) # Find page by path
|
38
|
+
page = storage.status_page( lang, path, 404 ) unless page && page.file # Render 404 page if there are no page, or there are no file
|
39
|
+
|
40
|
+
[ page[:status], page.render ] # Return page status and content
|
41
|
+
else
|
42
|
+
[ 404, "Page not found" ]
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
private
|
45
47
|
|
48
|
+
def create_storage( cfg )
|
49
|
+
Rayo::Storage.new( cfg )
|
50
|
+
end
|
51
|
+
|
46
52
|
def select_language
|
47
53
|
config.languages.first
|
48
54
|
end
|
49
55
|
|
50
56
|
def redirect_to_lang( path )
|
51
|
-
redirect [select_language, *path].join
|
57
|
+
redirect '/' + [ select_language, *path].join('/')
|
52
58
|
end
|
53
59
|
|
54
60
|
end
|
data/lib/rayo/config.rb
CHANGED
@@ -14,6 +14,7 @@ class Rayo::Config
|
|
14
14
|
@page_exts = ['.yml']
|
15
15
|
|
16
16
|
@filters = {}
|
17
|
+
@domains = []
|
17
18
|
|
18
19
|
# Default filters
|
19
20
|
add_filter('html'){|source| source }
|
@@ -28,15 +29,28 @@ class Rayo::Config
|
|
28
29
|
end
|
29
30
|
|
30
31
|
# Add tags defined in module
|
32
|
+
#
|
33
|
+
# @param [Module] module defining tags to use
|
31
34
|
def add_tags( tag_module )
|
32
35
|
@tagger.extend tag_module
|
33
36
|
end
|
34
37
|
|
35
38
|
# Add filter
|
39
|
+
#
|
40
|
+
# @param [String,Symbol] file extension
|
41
|
+
# @param [Proc] filter proc which accepts source and return it in processed form
|
36
42
|
def add_filter( ext, &filter )
|
37
43
|
@filters[".#{ext}"] = filter
|
38
44
|
end
|
39
45
|
|
46
|
+
# Add domain
|
47
|
+
#
|
48
|
+
# @param [String] domain name
|
49
|
+
# @param [Regexp,String] host pattern
|
50
|
+
def add_domain( name, exp = nil )
|
51
|
+
@domains << Rayo::Config::Domain.new( self, name, exp )
|
52
|
+
end
|
53
|
+
|
40
54
|
# Create new object containing all defined tags
|
41
55
|
def create_tagger
|
42
56
|
@tagger.clone
|
@@ -54,4 +68,14 @@ class Rayo::Config
|
|
54
68
|
@filters[ ext.to_s ]
|
55
69
|
end
|
56
70
|
|
71
|
+
def domain( host )
|
72
|
+
if @domains.empty?
|
73
|
+
self # No multidomain support
|
74
|
+
else
|
75
|
+
@domains.find { |domain| domain.matches? host }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
57
79
|
end
|
80
|
+
|
81
|
+
require File.join(File.dirname(__FILE__), 'config/domain.rb')
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class Rayo::Config::Domain
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@parent, :create_tagger, :languages, :page_exts, :renderable_exts, :filter
|
8
|
+
|
9
|
+
def initialize( parent, name, exp )
|
10
|
+
@parent = parent
|
11
|
+
@name = name
|
12
|
+
@exp = exp || Regexp.new( "^#{Regexp.quote( name )}\.?$" )
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?( host )
|
16
|
+
host =~ @exp
|
17
|
+
end
|
18
|
+
|
19
|
+
def directory( content_type )
|
20
|
+
if content_type == :pages
|
21
|
+
File.join( @parent.directory( content_type ), @name )
|
22
|
+
else
|
23
|
+
@parent.directory( content_type )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/rayo/models/page.rb
CHANGED
@@ -6,42 +6,59 @@ require File.join(File.dirname(__FILE__), '..', 'tag_context.rb')
|
|
6
6
|
|
7
7
|
class Rayo::Models::Page
|
8
8
|
|
9
|
-
attr_reader :storage, :parent, :path
|
9
|
+
attr_reader :storage, :parent, :lang, :path
|
10
10
|
|
11
|
-
def initialize( storage, parent, path )
|
11
|
+
def initialize( storage, parent, lang, path )
|
12
12
|
@storage = storage
|
13
13
|
@parent = parent
|
14
|
+
@lang = lang
|
14
15
|
@path = path
|
15
16
|
end
|
16
17
|
|
17
18
|
def descendant( relative_path )
|
18
|
-
relative_path.inject( self )
|
19
|
+
relative_path.inject( self ) do |page,slug|
|
20
|
+
return nil unless page
|
21
|
+
|
22
|
+
page.child( slug )
|
23
|
+
end
|
19
24
|
end
|
20
25
|
|
21
26
|
def children
|
22
|
-
@children ||= @storage.find_pages( directories ).map{|name| child( name ) }
|
27
|
+
@children ||= @storage.find_pages( directories, @lang ).map{|name| child( name ) }
|
23
28
|
end
|
24
29
|
|
25
30
|
def child( slug )
|
26
31
|
@children_cache ||= {}
|
27
32
|
return @children_cache[ slug ] if @children_cache.include? slug
|
28
33
|
|
29
|
-
page = Rayo::Models::Page.new( @storage, self, @path + [slug] )
|
34
|
+
page = Rayo::Models::Page.new( @storage, self, @lang, @path + [slug] )
|
30
35
|
page = nil if page.file.nil? && page.directories.empty?
|
31
36
|
|
32
37
|
@children_cache[ slug ] = page
|
33
38
|
end
|
34
39
|
|
35
40
|
def directories
|
36
|
-
@directories ||= @storage.find_page_dirs( @parent.directories, @path.last )
|
41
|
+
@directories ||= @storage.find_page_dirs( @parent.directories, @lang, @path.last )
|
37
42
|
end
|
38
43
|
|
39
44
|
def file
|
40
|
-
@file ||= @storage.find_page_file( @parent.directories, @path.last )
|
45
|
+
@file ||= @storage.find_page_file( @parent.directories, @lang, @path.last )
|
41
46
|
end
|
42
47
|
|
43
48
|
def parts
|
44
|
-
@parts ||= file ? @storage.find_page_parts( file ) : {}
|
49
|
+
@parts ||= file ? @storage.find_page_parts( file, @lang ) : {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_part( part_name, inherit = false )
|
53
|
+
page = self
|
54
|
+
part = self.parts[part_name]
|
55
|
+
|
56
|
+
while inherit && !part && page.parent do
|
57
|
+
page = page.parent
|
58
|
+
part = page.parts[part_name]
|
59
|
+
end
|
60
|
+
|
61
|
+
part
|
45
62
|
end
|
46
63
|
|
47
64
|
def params
|
@@ -68,7 +85,7 @@ class Rayo::Models::Page
|
|
68
85
|
end
|
69
86
|
|
70
87
|
def layout
|
71
|
-
@layout ||= @storage.layout( context['layout'] )
|
88
|
+
@layout ||= @storage.layout( @lang, context['layout'] )
|
72
89
|
end
|
73
90
|
|
74
91
|
def []( key )
|
@@ -88,7 +105,7 @@ class Rayo::Models::Page
|
|
88
105
|
private
|
89
106
|
|
90
107
|
def load_context( filename )
|
91
|
-
YAML::load( Erubis::Eruby.new(
|
108
|
+
YAML::load( Erubis::Eruby.new( @storage.load( filename ) ).result( params ) )
|
92
109
|
end
|
93
110
|
|
94
111
|
end
|
@@ -1,15 +1,17 @@
|
|
1
1
|
class Rayo::Models::Renderable
|
2
2
|
|
3
|
+
attr_reader :storage
|
3
4
|
attr_reader :file
|
4
5
|
attr_reader :filter
|
5
6
|
|
6
|
-
def initialize( file, filter )
|
7
|
+
def initialize( storage, file, filter )
|
8
|
+
@storage = storage
|
7
9
|
@file = file
|
8
10
|
@filter = filter
|
9
11
|
end
|
10
12
|
|
11
13
|
def source
|
12
|
-
@source ||=
|
14
|
+
@source ||= @storage.load( file )
|
13
15
|
end
|
14
16
|
|
15
17
|
def render( parser )
|
@@ -3,8 +3,8 @@ require File.join(File.dirname(__FILE__), 'status_page.rb')
|
|
3
3
|
|
4
4
|
class Rayo::Models::RootPage < Rayo::Models::Page
|
5
5
|
|
6
|
-
def initialize( storage )
|
7
|
-
super( storage, nil, [] )
|
6
|
+
def initialize( storage, lang )
|
7
|
+
super( storage, nil, lang, [] )
|
8
8
|
end
|
9
9
|
|
10
10
|
def directories
|
@@ -12,7 +12,7 @@ class Rayo::Models::RootPage < Rayo::Models::Page
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def file
|
15
|
-
@file ||= @storage.find_page_file( directories, 'index' )
|
15
|
+
@file ||= @storage.find_page_file( directories, @lang, 'index' )
|
16
16
|
end
|
17
17
|
|
18
18
|
def params
|
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), 'page.rb')
|
|
3
3
|
class Rayo::Models::StatusPage < Rayo::Models::Page
|
4
4
|
|
5
5
|
def initialize( storage, root, path, status )
|
6
|
-
super( storage, root, path )
|
6
|
+
super( storage, root, root.lang, path )
|
7
7
|
|
8
8
|
@status = status
|
9
9
|
end
|
@@ -13,14 +13,14 @@ class Rayo::Models::StatusPage < Rayo::Models::Page
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def file
|
16
|
-
@file ||= @storage.find_page_file( @parent.directories, @status.to_s )
|
16
|
+
@file ||= @storage.find_page_file( @parent.directories, @lang, @status.to_s )
|
17
17
|
end
|
18
18
|
|
19
19
|
def context
|
20
20
|
return @context if @context
|
21
21
|
|
22
22
|
@context = @parent ? @parent.context.merge({ 'status' => @status }) : { 'status' => @status }
|
23
|
-
@context.merge! load_context( file
|
23
|
+
@context.merge! load_context( file ) if file
|
24
24
|
@context
|
25
25
|
end
|
26
26
|
|
data/lib/rayo/storage.rb
CHANGED
@@ -5,105 +5,134 @@ class Rayo::Storage
|
|
5
5
|
|
6
6
|
attr_reader :config
|
7
7
|
|
8
|
-
def initialize( config
|
8
|
+
def initialize( config )
|
9
9
|
@config = config
|
10
10
|
|
11
|
-
@
|
12
|
-
@lang_prefix = '.' + lang
|
13
|
-
|
11
|
+
@roots = {}
|
14
12
|
@layouts = {}
|
15
13
|
@snippets = {}
|
16
14
|
end
|
17
15
|
|
18
|
-
def snippet( name )
|
19
|
-
@
|
16
|
+
def snippet( lang, name )
|
17
|
+
@snippets["#{lang}|#{name}"] ||= find_renderable( :snippets, lang, name.to_s ) || raise( "Snippet '#{name}' not found" )
|
20
18
|
end
|
21
19
|
|
22
|
-
def layout( name )
|
23
|
-
@layouts[name
|
20
|
+
def layout( lang, name )
|
21
|
+
@layouts["#{lang}|#{name}"] ||= find_renderable( :layouts, lang, name.to_s ) || raise( "Layout '#{name}' not found" )
|
24
22
|
end
|
25
23
|
|
26
|
-
|
27
|
-
|
24
|
+
# Retrieves root page for specific language
|
25
|
+
#
|
26
|
+
# @param [String,Symbol] page language
|
27
|
+
# @return [Rayo::Models::RootPage] root page
|
28
|
+
def root_page( lang )
|
29
|
+
@roots[ lang.to_s ] ||= Rayo::Models::RootPage.new( self, lang.to_s )
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
+
# Retrieves page for specific language and path
|
33
|
+
#
|
34
|
+
# @param [String,Symbol] page language
|
35
|
+
# @param [Array<String>] page path
|
36
|
+
# @return [Rayo::Models::Page] page
|
37
|
+
def page( lang, path )
|
38
|
+
root_page( lang ).descendant( path )
|
32
39
|
end
|
33
40
|
|
34
|
-
|
35
|
-
|
41
|
+
# Retrieves status page for specific language and path
|
42
|
+
#
|
43
|
+
# @param [String,Symbol] page language
|
44
|
+
# @param [Array<String>] page path
|
45
|
+
# @return [Rayo::Models::StatusPage] status page
|
46
|
+
def status_page( lang, path, status )
|
47
|
+
Rayo::Models::StatusPage.new( self, root_page( lang ), path, status )
|
36
48
|
end
|
37
49
|
|
38
|
-
def find_renderable( type, name )
|
39
|
-
if file = find_file( [config.directory( type )], name, config.renderable_exts )
|
50
|
+
def find_renderable( type, lang, name )
|
51
|
+
if file = find_file( [config.directory( type )], lang, name, config.renderable_exts )
|
40
52
|
renderable( file, File.extname( file ) )
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
44
|
-
def find_page_file( dirs, slug )
|
45
|
-
find_file( dirs, slug, config.page_exts )
|
56
|
+
def find_page_file( dirs, lang, slug )
|
57
|
+
find_file( dirs, lang, slug, config.page_exts )
|
46
58
|
end
|
47
59
|
|
48
|
-
def find_page_dirs( dirs, slug )
|
49
|
-
find_files dirs, slug, ['']
|
60
|
+
def find_page_dirs( dirs, lang, slug )
|
61
|
+
find_files( dirs, lang, slug, [''] )
|
50
62
|
end
|
51
63
|
|
52
|
-
def find_pages( dirs )
|
53
|
-
|
64
|
+
def find_pages( dirs, lang )
|
65
|
+
res = []
|
66
|
+
glob_files dirs, lang, '*', config.page_exts + [''] do |file,base,ext|
|
67
|
+
elems = base.split('.')
|
68
|
+
elem_name = elems[0]
|
69
|
+
elem_lang = elems[1]
|
70
|
+
|
71
|
+
res << elem_name unless elem_name[0..0] == '%' || (dirs == ([ @config.directory :pages ]) && (elem_name == 'index' || elem_name =~ /^\d+$/ )) || (elem_lang && elem_lang != lang)
|
72
|
+
end
|
73
|
+
res.uniq
|
54
74
|
end
|
55
75
|
|
56
|
-
def find_page_parts( page_file )
|
76
|
+
def find_page_parts( page_file, lang )
|
57
77
|
parts = {}
|
58
|
-
page_file_base = File.basename( page_file
|
59
|
-
glob_files File.dirname( page_file ), page_file_base + '*', config.renderable_exts do |file,base,ext|
|
78
|
+
page_file_base = File.basename( page_file ).split('.').first
|
79
|
+
glob_files File.dirname( page_file ), lang, page_file_base + '*', config.renderable_exts do |file,base,ext|
|
60
80
|
elems = base.split('.')
|
61
81
|
|
62
82
|
if elems.shift == page_file_base # Remove base (slug or variable)
|
63
83
|
if elems.size == 0 # There are no part name and language
|
64
84
|
parts[ 'body' ] ||= renderable( file, ext )
|
65
85
|
elsif elems.size == 1 # There are no language or no part name
|
66
|
-
|
67
|
-
|
86
|
+
if elems[0] == lang
|
87
|
+
parts[ 'body' ] ||= renderable( file, ext )
|
88
|
+
else
|
89
|
+
parts[ elems[0] ] ||= renderable( file, ext )
|
90
|
+
end
|
68
91
|
else
|
69
|
-
parts[ elems[0] ] ||= renderable( file, ext ) if elems[1] ==
|
92
|
+
parts[ elems[0] ] ||= renderable( file, ext ) if elems[1] == lang
|
70
93
|
end
|
71
94
|
end
|
72
95
|
end
|
73
96
|
parts
|
74
97
|
end
|
75
98
|
|
99
|
+
def load( file )
|
100
|
+
File.read( file )
|
101
|
+
end
|
102
|
+
|
76
103
|
private
|
77
104
|
|
78
105
|
def renderable( file, ext )
|
79
|
-
Rayo::Models::Renderable.new( file, config.filter( ext ) || raise( "Filter for '#{ext} not found" ) )
|
106
|
+
Rayo::Models::Renderable.new( self, file, config.filter( ext ) || raise( "Filter for '#{ext} not found" ) )
|
80
107
|
end
|
81
108
|
|
82
109
|
# Find first file with given name (or variable) and extension from given set
|
83
|
-
def find_file( dirs, name, exts )
|
84
|
-
glob_files( dirs, name, exts ) { |file,base,ext| return file }
|
110
|
+
def find_file( dirs, lang, name, exts )
|
111
|
+
glob_files( dirs, lang, name, exts ) { |file,base,ext| return file }
|
85
112
|
nil
|
86
113
|
end
|
87
114
|
|
88
115
|
# Find files with given name (or variable) and extension from given set
|
89
|
-
def find_files( dirs, name, exts )
|
116
|
+
def find_files( dirs, lang, name, exts )
|
90
117
|
results = []
|
91
|
-
glob_files( dirs, name, exts ) { |file,base,ext| results << file }
|
118
|
+
glob_files( dirs, lang, name, exts ) { |file,base,ext| results << file }
|
92
119
|
results
|
93
120
|
end
|
94
121
|
|
95
|
-
def glob_files( dirs, name, exts, &block )
|
96
|
-
|
122
|
+
def glob_files( dirs, lang, name, exts, &block )
|
123
|
+
lang_prefix = "." + lang
|
124
|
+
|
125
|
+
glob dirs, name + lang_prefix, //, exts, &block # Search with given name and language
|
97
126
|
glob dirs, name, //, exts, &block # Search with given name without language
|
98
|
-
glob dirs, '%*' +
|
127
|
+
glob dirs, '%*' + lang_prefix, /^%.+\.#{lang}$/, exts, &block # Search with variable and language
|
99
128
|
glob dirs, '%*', /^%.+$/, exts, &block # Search with variable without language
|
100
129
|
end
|
101
130
|
|
102
131
|
def glob( dirs, mask_wo_ext, base_regexp, exts )
|
103
|
-
mask = mask_wo_ext + ext_mask( exts )
|
132
|
+
mask = mask_wo_ext + ext_mask( mask_wo_ext, exts )
|
104
133
|
|
105
134
|
dirs.each do |dir|
|
106
|
-
|
135
|
+
dir_glob File.join( dir, mask ) do |file|
|
107
136
|
ext = File.extname( file )
|
108
137
|
base = File.basename( file, ext )
|
109
138
|
|
@@ -112,7 +141,11 @@ class Rayo::Storage
|
|
112
141
|
end
|
113
142
|
end
|
114
143
|
|
115
|
-
def
|
144
|
+
def dir_glob( mask, &block )
|
145
|
+
Dir.glob mask, &block
|
146
|
+
end
|
147
|
+
|
148
|
+
def ext_mask( mask_wo_ext, exts )
|
116
149
|
if exts.size == 1 # Only one extension
|
117
150
|
exts.first # Use it in mask
|
118
151
|
elsif !exts.include? '' # No empty extension
|
data/lib/rayo/tag_context.rb
CHANGED
@@ -8,6 +8,7 @@ class Rayo::TagContext < Radius::Context
|
|
8
8
|
@page = page
|
9
9
|
|
10
10
|
globals.page = @page
|
11
|
+
globals.storage = @page.storage
|
11
12
|
|
12
13
|
tagger = @page.storage.config.create_tagger
|
13
14
|
tagger.methods.each do |name|
|
@@ -15,4 +16,20 @@ class Rayo::TagContext < Radius::Context
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
def render_tag(name, attributes = {}, &block)
|
20
|
+
super
|
21
|
+
rescue Exception => e
|
22
|
+
error("#{e.message} <pre>#{e.backtrace.join("\n")}</pre>")
|
23
|
+
end
|
24
|
+
|
25
|
+
def tag_missing(name, attributes = {}, &block)
|
26
|
+
super
|
27
|
+
rescue Radius::UndefinedTagError => e
|
28
|
+
error("#{e.message} <pre>#{e.backtrace.join("\n")}</pre>")
|
29
|
+
end
|
30
|
+
|
31
|
+
def error( text )
|
32
|
+
"<strong class=\"error\">#{text}</strong>"
|
33
|
+
end
|
34
|
+
|
18
35
|
end
|
@@ -3,44 +3,55 @@ module Rayo::Tags::ContentTags
|
|
3
3
|
include Rayo::Taggable
|
4
4
|
|
5
5
|
tag 'content' do |tag|
|
6
|
-
|
7
|
-
inherit = tag.attr['inherit'] == 'true'
|
8
|
-
|
9
|
-
page = tag.locals.page
|
10
|
-
part = page.parts[part_name]
|
11
|
-
|
12
|
-
while inherit && !part && page.parent do
|
13
|
-
page = page.parent
|
14
|
-
part = page.parts[part_name]
|
15
|
-
end
|
16
|
-
|
17
|
-
if part
|
6
|
+
if part = find_part( tag )
|
18
7
|
part.render( tag.globals.page.parser )
|
19
8
|
else
|
20
|
-
error "No part '#{
|
9
|
+
error "No part '#{tag.attr['part'] || 'body'}' found for page '#{tag.locals.page.path.join('/')}'"
|
21
10
|
end
|
22
11
|
end
|
23
12
|
|
13
|
+
tag 'if_content' do |tag|
|
14
|
+
find_part( tag ) && tag.expand || ''
|
15
|
+
end
|
16
|
+
|
24
17
|
tag 'content_for_layout' do |tag|
|
25
|
-
|
18
|
+
tag.globals.content_stack ||= [] # Prepare the stacks
|
19
|
+
tag.globals.content_stack.pop || send( 'tag:content', tag )
|
26
20
|
end
|
27
21
|
|
28
|
-
tag '
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
tag 'layout' do |tag|
|
23
|
+
if name = tag.attr['name'].strip
|
24
|
+
tag.globals.layout_stack ||= [] # Prepare layout stack
|
25
|
+
tag.globals.content_stack ||= [] # Prepare content stack
|
26
|
+
|
27
|
+
if layout = tag.globals.storage.layout( tag.globals.page.lang, name )
|
28
|
+
tag.globals.layout_stack << name # Track this layout on the stack
|
29
|
+
tag.globals.content_stack << tag.expand # Save contents of inside_layout for later insertion
|
30
|
+
|
31
|
+
layout.render( tag.globals.page.parser )
|
32
|
+
else
|
33
|
+
error "Parent layout '#{name.strip}' not found for 'layout' tag"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
error "'layout' tag must contain a 'name' attribute"
|
37
|
+
end
|
33
38
|
end
|
34
39
|
|
35
|
-
tag '
|
36
|
-
|
40
|
+
tag 'snippet' do |tag|
|
41
|
+
snippet_name = tag.attr['name']
|
42
|
+
snippet = tag.globals.storage.snippet( tag.globals.page.lang, snippet_name )
|
43
|
+
|
44
|
+
if snippet
|
45
|
+
snippet.render( tag.globals.page.parser )
|
46
|
+
else
|
47
|
+
error "No snippet '#{snippet_name}' found"
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
result
|
51
|
+
private
|
52
|
+
|
53
|
+
def find_part( tag )
|
54
|
+
tag.locals.page.find_part( tag.attr['part'] || 'body', tag.attr['inherit'] == 'true' )
|
44
55
|
end
|
45
56
|
|
46
57
|
end
|
@@ -6,20 +6,48 @@ module Rayo::Tags::PropertyTags
|
|
6
6
|
tag.locals.page.context['title']
|
7
7
|
end
|
8
8
|
|
9
|
+
tag 'description' do |tag|
|
10
|
+
tag.locals.page.context['description']
|
11
|
+
end
|
12
|
+
|
9
13
|
tag 'path' do |tag|
|
10
|
-
|
14
|
+
basepath = tag.globals.page.path
|
11
15
|
path = tag.locals.page.path
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
if basepath.empty?
|
18
|
+
[tag.locals.page.lang, *path].join('/')
|
19
|
+
else
|
20
|
+
basepath = basepath[0..-2]
|
21
|
+
|
22
|
+
i = 0
|
23
|
+
while i < path.size && i < basepath.size && path[i] == basepath[i] do
|
24
|
+
i = i + 1
|
25
|
+
end
|
17
26
|
|
18
|
-
|
27
|
+
'../' * (basepath.size - i) + path[i..-1].join('/')
|
28
|
+
end
|
19
29
|
end
|
20
30
|
|
21
31
|
tag 'link' do |tag|
|
22
|
-
"<a href=\"#{send 'tag:path', tag}\">#{tag.
|
32
|
+
"<a href=\"#{send 'tag:path', tag}\">#{tag.single? ? send( 'tag:title', tag ) : tag.expand}</a>"
|
33
|
+
end
|
34
|
+
|
35
|
+
tag 'if_url' do |tag|
|
36
|
+
if_url( tag ) && tag.expand || ''
|
37
|
+
end
|
38
|
+
|
39
|
+
tag 'unless_url' do |tag|
|
40
|
+
if_url( tag ) && '' || tag.expand
|
41
|
+
end
|
42
|
+
|
43
|
+
tag 'date' do |tag|
|
44
|
+
Time.now.strftime(tag.attr['format'] || '%A, %B %d, %Y')
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def if_url( tag )
|
50
|
+
tag.locals.page.path =~ Regexp.new( tag.attr['matches'] )
|
23
51
|
end
|
24
52
|
|
25
53
|
end
|
data/lib/rayo/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rayo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Alexey Noskov
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-02 00:00:00 +03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -61,17 +61,48 @@ dependencies:
|
|
61
61
|
version: "1.0"
|
62
62
|
type: :runtime
|
63
63
|
version_requirements: *id003
|
64
|
-
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: rspec
|
66
|
+
prerelease: false
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 2
|
75
|
+
- 0
|
76
|
+
version: "2.0"
|
77
|
+
type: :development
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: rack-test
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id005
|
93
|
+
description: Lightweight CMS based on Sinatra framework, where data are stored in file system (and so may be Git-powered) and enhanced using Radius gem.
|
65
94
|
email:
|
66
95
|
- alexey.noskov@gmail.com
|
67
96
|
executables: []
|
68
97
|
|
69
98
|
extensions: []
|
70
99
|
|
71
|
-
extra_rdoc_files:
|
72
|
-
|
100
|
+
extra_rdoc_files:
|
101
|
+
- README.rdoc
|
102
|
+
- MIT-LICENSE
|
73
103
|
files:
|
74
104
|
- lib/rayo.rb
|
105
|
+
- lib/rayo/config/domain.rb
|
75
106
|
- lib/rayo/storage.rb
|
76
107
|
- lib/rayo/application.rb
|
77
108
|
- lib/rayo/version.rb
|
@@ -85,13 +116,20 @@ files:
|
|
85
116
|
- lib/rayo/tags/navigation_tags.rb
|
86
117
|
- lib/rayo/config.rb
|
87
118
|
- lib/rayo/tag_context.rb
|
119
|
+
- MIT-LICENSE
|
120
|
+
- README.rdoc
|
88
121
|
has_rdoc: true
|
89
122
|
homepage: http://github.com/alno/rayo
|
90
123
|
licenses: []
|
91
124
|
|
92
125
|
post_install_message:
|
93
|
-
rdoc_options:
|
94
|
-
|
126
|
+
rdoc_options:
|
127
|
+
- --line-numbers
|
128
|
+
- --inline-source
|
129
|
+
- --title
|
130
|
+
- Rayo CMS
|
131
|
+
- --main
|
132
|
+
- README.rdoc
|
95
133
|
require_paths:
|
96
134
|
- lib
|
97
135
|
required_ruby_version: !ruby/object:Gem::Requirement
|