rayo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|