razor 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Ferreira, Christopher
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the Software), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift './lib'
4
+
5
+ require 'razor/runner'
6
+
7
+ Razor::Runner.new
@@ -0,0 +1,58 @@
1
+ require 'version'
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+
5
+ require 'razor/generable'
6
+ require 'highline'
7
+
8
+ module Razor
9
+
10
+ class << self
11
+ attr :get
12
+ end
13
+
14
+ def self.create(name)
15
+ Dir.mkdir name
16
+ end
17
+
18
+ def self.generate(src, dest)
19
+ site = Site.new(src, dest)
20
+ FileUtils.rm_r(dest) rescue nil
21
+ site.generate
22
+ end
23
+
24
+ def self.server(port, src)
25
+ require 'rack'
26
+ port = (port and port.to_i or 9000)
27
+ on_request = lambda do |env|
28
+ page = Razor.http_request src, env['PATH_INFO']
29
+ p page.dest_name rescue nil
30
+ if page
31
+ [
32
+ 200,
33
+ { 'Content-Type' =>
34
+ Rack::Mime.mime_type(page.dest_ext) },
35
+ [ page.contents ]
36
+ ]
37
+ else
38
+ [
39
+ 404,
40
+ { 'Content-Type' => 'text/html' },
41
+ [ 'Page Not Found' ]
42
+ ]
43
+ end
44
+ end
45
+ Rack::Handler::WEBrick.run(on_request, :Port => port) { |server|
46
+ trap(:INT) { server.stop }
47
+ trap(:TERM) { server.stop }
48
+ }
49
+ end
50
+
51
+ def self.http_request(src, url)
52
+ url = url[1..-1].sub(/\/+/, '/')
53
+ site = Site.new(src, '')
54
+ file = site.http(url)
55
+ file and file.request
56
+ end
57
+
58
+ end
@@ -0,0 +1,142 @@
1
+ require 'fileutils'
2
+
3
+ require 'razor/template'
4
+
5
+ module Razor
6
+
7
+ class Generable
8
+
9
+ attr_reader :parent, :src_name
10
+
11
+ def initialize(parent, src_name)
12
+ @parent = parent
13
+ @src_name = src_name
14
+ end
15
+
16
+ alias dest_name src_name
17
+
18
+ def src
19
+ @src ||= File.join(parent.src, src_name)
20
+ end
21
+
22
+ def dest
23
+ @dest ||= File.join(parent.dest, dest_name)
24
+ end
25
+
26
+ def url
27
+ @url ||= File.join(parent.url, dest_name)
28
+ end
29
+
30
+ def http(url)
31
+ url.empty? and return self
32
+ return nil
33
+ end
34
+
35
+ end
36
+
37
+ class GenerableFile < Generable
38
+
39
+ def contents
40
+ File.read(src, :mode=>'rb')
41
+ end
42
+
43
+ def generate
44
+ File.open(dest, 'wb') { |f| f << contents }
45
+ end
46
+
47
+ def request
48
+ self
49
+ end
50
+
51
+ def dest_ext
52
+ File.extname(dest_name)
53
+ end
54
+ end
55
+
56
+ class Directory < Generable
57
+
58
+ attr_reader :files
59
+
60
+ def initialize(parent, src_name)
61
+ super(parent, src_name)
62
+ @files = Dir.entries(src).reject { |filename|
63
+ filename =~ /^[\._].*/ or filename=='Rakefile'
64
+ }.map { |filename|
65
+ if File.directory? File.join(src,filename)
66
+ Directory.new(self, filename)
67
+ elsif filename =~ /^[^~].*\.rb$/
68
+ TemplateFile.new(self, filename)
69
+ else
70
+ RegularFile.new(self, filename)
71
+ end
72
+ }
73
+ end
74
+
75
+ def generate
76
+ Dir.mkdir(dest)
77
+ @files.each(&:generate)
78
+ end
79
+
80
+ def http(url)
81
+ s = super(url)
82
+ s and return s
83
+ name, *rest = url.split('/')
84
+ file = @files.find { |f| f.dest_name == name }
85
+ file or return nil
86
+ file.http(rest*'/')
87
+ end
88
+
89
+ def request
90
+ @files.find { |f| f.dest_name == 'index.html' }
91
+ end
92
+ end
93
+
94
+ class Site < Directory
95
+
96
+ attr_reader :src, :dest
97
+ def initialize(src, dest)
98
+ Template.reset_instances!
99
+ @src, @dest = src, dest
100
+ layouts = File.join(src, '_layout/')
101
+ Template.path = layouts
102
+ Dir.chdir(layouts) { load './layouts.rb' }
103
+ super(nil, nil)
104
+ end
105
+
106
+ def url
107
+ '/'
108
+ end
109
+ end
110
+
111
+ class RegularFile < GenerableFile
112
+
113
+ def dest_name
114
+ # delete first '~' if needed.
115
+ super =~ /([^~].+)/
116
+ return $1
117
+ end
118
+
119
+ def generate
120
+ FileUtils.cp(src, dest)
121
+ end
122
+ end
123
+
124
+ class TemplateFile < GenerableFile
125
+
126
+ def initialize(parent, src_name)
127
+ super(parent, src_name)
128
+ @template = Template.eval(self)
129
+ end
130
+
131
+ def dest_name
132
+ ext = @template.class.get_extension
133
+ src_name.sub(/rb$/, ext)
134
+ end
135
+
136
+ def contents
137
+ @template.render
138
+ end
139
+
140
+ end
141
+
142
+ end
@@ -0,0 +1,120 @@
1
+ require 'bluecloth'
2
+ require 'sass'
3
+
4
+ module Razor
5
+
6
+ class Renderer
7
+
8
+ class Getter
9
+
10
+ class << self
11
+
12
+ attr_reader :renderers, :extensions
13
+
14
+ def define_renderer(name, renderer)
15
+ @renderers[name] = renderer
16
+ class_eval(<<-END)
17
+ def #{name}(string)
18
+ self.class.renderers[#{name.inspect}].new(string)
19
+ end
20
+ END
21
+ end
22
+
23
+ def define_extension(name, renderer)
24
+ @extensions[".#{name}"] = renderer
25
+ end
26
+
27
+ end
28
+
29
+ @renderers = {}
30
+ @extensions = {}
31
+
32
+ attr :renderer
33
+
34
+ def initialize(path, proc)
35
+ @path = path
36
+ if proc
37
+ @renderer = instance_exec &proc
38
+ else
39
+ @renderer = instance_exec { nil }
40
+ end
41
+ if @renderer.is_a? String or @renderer.is_a? NilClass
42
+ @renderer = Renderer.new(@renderer)
43
+ end
44
+ return @renderer
45
+ end
46
+
47
+ def from_file(filename)
48
+ filepath = File.join(@path, filename)
49
+ str = (File.read(filepath, :mode=>'rb') rescue nil)
50
+ ext = File.extname filename
51
+ renderer = self.class.extensions[ext]
52
+ renderer ||= Renderer
53
+ return renderer.new(str)
54
+ end
55
+
56
+ end
57
+
58
+ class << self
59
+
60
+ def get(path, proc)
61
+ Getter.new(path, proc).renderer
62
+ end
63
+
64
+ def name_ext(n)
65
+ name(n)
66
+ extension(n)
67
+ end
68
+
69
+ def name(n)
70
+ Getter.define_renderer(n, self)
71
+ end
72
+
73
+ def extension(ext)
74
+ Getter.define_extension(ext, self)
75
+ end
76
+ end
77
+
78
+ attr :string
79
+
80
+ def initialize(string)
81
+ @string = string
82
+ end
83
+
84
+ def layout_render(context)
85
+ @string.nil? and return context['yield']
86
+ render(context)
87
+ end
88
+
89
+ def valid?
90
+ !!@string
91
+ end
92
+
93
+ def render(context)
94
+ @string.nil? and return ''
95
+ Mustache.render(@string, context)
96
+ end
97
+
98
+ end
99
+
100
+ class BlueRenderer < Renderer
101
+
102
+ name_ext 'markdown'
103
+
104
+ def render(context)
105
+ BlueCloth.new(super(context)).to_html
106
+ end
107
+
108
+ end
109
+
110
+ class ScssRenderer < Renderer
111
+
112
+ name_ext 'scss'
113
+
114
+ def render(context)
115
+ Sass::Engine.new(super(context), :syntax=>:scss ).render
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -0,0 +1,82 @@
1
+ require 'optparse'
2
+ require 'highline'
3
+
4
+ require 'razor'
5
+
6
+ module Razor
7
+
8
+ HL = HighLine.new
9
+
10
+ class Runner
11
+
12
+ def initialize
13
+ @options = {}
14
+
15
+ @options_parser = OptionParser.new { |opt|
16
+
17
+ opt.banner = "Usage: razor COMMAND"
18
+
19
+ opt.separator ""
20
+ opt.separator "Commands :"
21
+ opt.separator " generate [OPTIONS] [SOURCE DESTINATION] : Generates the web directory from source."
22
+ opt.separator " server [OPTIONS] [SOURCE] : Runs a webrick server which renders page on the fly."
23
+ opt.separator ""
24
+ opt.separator "Options : "
25
+
26
+ opt.on("-p PORT", "--port PORT", "Set the local server port") do |port|
27
+ @options[:port] = port
28
+ end
29
+
30
+ opt.on_tail("-v", "--version", "Show Version") do
31
+ @options[:version] = true
32
+ end
33
+
34
+ opt.on_tail("-h", "--help", "Show this message") do
35
+ @options[:help] = true
36
+ end
37
+
38
+ opt.separator ""
39
+
40
+ }
41
+ @options_parser.parse!
42
+
43
+ unless ARGV.empty?
44
+ args = ARGV.clone
45
+ ARGV.replace []
46
+ if respond_to? args[0]
47
+ send *args
48
+ else
49
+ HL.say "Unknown Command #{args[0]}"
50
+ HL.say @options_parser.to_s
51
+ end
52
+ else
53
+ if @options[:version]
54
+ HL.say Razor::Version
55
+ else
56
+ HL.say @options_parser.to_s
57
+ end
58
+ end
59
+ end
60
+
61
+ def _check_arguments(bool)
62
+ return if bool
63
+ HL.say 'Arguments Error'
64
+ HL.say @options_parser.to_s
65
+ exit
66
+ end
67
+
68
+ def generate(*args)
69
+ _check_arguments args.size < 3
70
+ args = ['.', '_site'] if args.empty?
71
+ args << File.join(args[0], '_site') if args.size==1
72
+ Razor.generate *args
73
+ end
74
+
75
+ def server(url=nil)
76
+ url = '.' if url.nil?
77
+ Razor.server(@options[:port], url)
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,192 @@
1
+ require 'mustache'
2
+ require 'razor/renderer'
3
+
4
+ module Razor
5
+
6
+ class Template
7
+
8
+ class << self
9
+
10
+ attr_reader :definition, :playout, :instances
11
+
12
+ def inherited(subclass)
13
+ @subclasses << subclass
14
+ subclass.class_initialize
15
+ end
16
+
17
+ def class_initialize
18
+ @subclasses = []
19
+ @definition = Class.new(superclass.definition)
20
+ @playout = superclass.playout
21
+ @fields = {}
22
+ @instances = []
23
+ end
24
+
25
+ def reset_instances!
26
+ @instances = []
27
+ @subclasses.each(&:reset_instances!)
28
+ end
29
+
30
+ def eval(templatefile)
31
+ @@last_evaluated = nil
32
+ @@last_templatefile = templatefile
33
+ Kernel.eval(File.read(templatefile.src), TOPLEVEL_BINDING)
34
+ return @@last_evaluated
35
+ end
36
+
37
+ def extension(ext)
38
+ @extension = ext
39
+ end
40
+
41
+ def path=(path)
42
+ @@path = path
43
+ end
44
+
45
+ def get_extension
46
+ if superclass.respond_to? :get_extension
47
+ @extension or superclass.get_extension
48
+ else
49
+ @extension
50
+ end
51
+ end
52
+
53
+ def layout(str=nil, &block)
54
+ @playout = str ? lambda { str } : block
55
+ end
56
+
57
+ def field(name, default=nil)
58
+ if name =~ /^(field|contents)$/
59
+ raise "Try to create a #{name.inspect} field"
60
+ end
61
+ attr_accessor name
62
+ @fields["@#{name}"] = default
63
+ @definition.field(name)
64
+ end
65
+
66
+ def fields
67
+ if superclass.respond_to? :fields
68
+ superclass.fields.merge(@fields)
69
+ else
70
+ @fields
71
+ end
72
+ end
73
+
74
+ def new(*args, &block)
75
+ instance = super(*args, &block)
76
+ @instances << instance
77
+ @@last_evaluated = instance
78
+ return instance
79
+ end
80
+
81
+ def all
82
+ @subclasses.inject(@instances) { |ary, subclass|
83
+ ary + subclass.instances
84
+ }
85
+ end
86
+
87
+ def layout_render(instance)
88
+ render(Mustache::Context.new(instance))
89
+ end
90
+
91
+ def render(context)
92
+ renderer = Renderer.get(@@path, @playout)
93
+ if superclass.respond_to? :render
94
+ if renderer.valid?
95
+ context.push('yield' => renderer.layout_render(context))
96
+ end
97
+ superclass.render(context)
98
+ else
99
+ if renderer.valid?
100
+ renderer.layout_render(context)
101
+ else
102
+ context['yield']
103
+ end
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ @extension = ''
110
+ @playout = nil
111
+ @fields = {}
112
+ @instances = []
113
+ @subclasses = []
114
+
115
+ attr_accessor :contents
116
+
117
+ def initialize(*args, &block)
118
+ @templatefile = @@last_templatefile
119
+ @contents = lambda { '' }
120
+ self.class.fields.each { |iv|
121
+ instance_variable_set(*iv)
122
+ }
123
+ if block_given?
124
+ self.class.definition.new(self, &block)
125
+ end
126
+ end
127
+
128
+ def url
129
+ @templatefile.url
130
+ end
131
+
132
+ def field(name, value)
133
+ (class<<self; self; end).class_eval { attr name }
134
+ instance_variable_set("@#{name}", value)
135
+ end
136
+
137
+ def yield
138
+ Renderer.get(@templatefile.parent.src, @contents).render(self)
139
+ end
140
+
141
+ def render
142
+ self.class.layout_render(self)
143
+ end
144
+
145
+ @definition = Class.new {
146
+
147
+ def self.field(name)
148
+ class_eval(<<-END)
149
+ def #{name}(val)
150
+ @tpl.#{name} = val
151
+ end
152
+ END
153
+ end
154
+
155
+ def initialize(tpl, &block)
156
+ @tpl = tpl
157
+ instance_exec &block
158
+ end
159
+
160
+ def field(name, value)
161
+ @tpl.field(name, value)
162
+ end
163
+
164
+ def contents(str=nil, &block)
165
+ @tpl.contents = str ? lambda { str } : block
166
+ end
167
+
168
+ def template_eval(&block)
169
+ @tpl.instance_eval(&block)
170
+ end
171
+
172
+ }
173
+
174
+ end
175
+
176
+ class Page < Template
177
+
178
+ extension 'html'
179
+
180
+ layout { from_file 'page.html' }
181
+
182
+ end
183
+
184
+ class StyleSheet < Template
185
+
186
+ extension 'css'
187
+
188
+ layout { from_file 'stylesheet.css' }
189
+
190
+ end
191
+
192
+ end
@@ -0,0 +1,3 @@
1
+ module Razor
2
+ Version = '0.4.0'
3
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: razor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ferreira Christopher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-03-09 00:00:00.000000000 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: commander
17
+ requirement: &18858180 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '4'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *18858180
26
+ - !ruby/object:Gem::Dependency
27
+ name: mustache
28
+ requirement: &18857660 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *18857660
37
+ - !ruby/object:Gem::Dependency
38
+ name: rack
39
+ requirement: &18857180 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *18857180
48
+ description: ! '''Razor is a static website generator. Describe layouts and pages
49
+ in ruby. Write them with html and Mustache.
50
+
51
+ The concept behind razor is simple : layouts are described by classes, nested layouts
52
+ by subclasses and pages by instances of these classes.''
53
+
54
+ '
55
+ email: aumgn@free.fr
56
+ executables:
57
+ - razor
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - README
62
+ - LICENSE
63
+ - lib/razor.rb
64
+ - lib/razor/generable.rb
65
+ - lib/razor/runner.rb
66
+ - lib/razor/renderer.rb
67
+ - lib/razor/template.rb
68
+ - lib/version.rb
69
+ - bin/razor
70
+ has_rdoc: true
71
+ homepage: https://github.com/aumgn/razor
72
+ licenses:
73
+ - MIT License. See LICENSE file.
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.5.2
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Razor is a static website generator. Describe layouts and pages in ruby.
96
+ Write them with html and Mustache.
97
+ test_files: []