nagoro 2009.05
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +446 -0
- data/MANIFEST +51 -0
- data/README.markdown +189 -0
- data/Rakefile +29 -0
- data/bin/nagoro +83 -0
- data/doc/COPYING +56 -0
- data/doc/GPL +340 -0
- data/doc/LEGAL +2 -0
- data/example/element/Html.nage +8 -0
- data/example/hello.nag +3 -0
- data/example/morpher.nag +23 -0
- data/lib/nagoro.rb +22 -0
- data/lib/nagoro/binding.rb +8 -0
- data/lib/nagoro/element.rb +46 -0
- data/lib/nagoro/pipe.rb +12 -0
- data/lib/nagoro/pipe/base.rb +56 -0
- data/lib/nagoro/pipe/compile.rb +30 -0
- data/lib/nagoro/pipe/element.rb +70 -0
- data/lib/nagoro/pipe/include.rb +36 -0
- data/lib/nagoro/pipe/instruction.rb +64 -0
- data/lib/nagoro/pipe/localization.rb +60 -0
- data/lib/nagoro/pipe/morph.rb +95 -0
- data/lib/nagoro/pipe/tidy.rb +49 -0
- data/lib/nagoro/scanner.rb +97 -0
- data/lib/nagoro/template.rb +89 -0
- data/lib/nagoro/tidy.rb +49 -0
- data/lib/nagoro/version.rb +3 -0
- data/nagoro.gemspec +28 -0
- data/spec/core_extensions.rb +33 -0
- data/spec/example/hello.rb +13 -0
- data/spec/helper.rb +19 -0
- data/spec/nagoro/listener/base.rb +19 -0
- data/spec/nagoro/pipe/compile.rb +31 -0
- data/spec/nagoro/pipe/element.rb +46 -0
- data/spec/nagoro/pipe/include.rb +17 -0
- data/spec/nagoro/pipe/instruction.rb +32 -0
- data/spec/nagoro/pipe/morph.rb +47 -0
- data/spec/nagoro/pipe/tidy.rb +23 -0
- data/spec/nagoro/template.rb +105 -0
- data/spec/nagoro/template/full.nag +26 -0
- data/spec/nagoro/template/hello.nag +1 -0
- data/tasks/bacon.rake +49 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/gem.rake +22 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/grancher.rake +12 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +19 -0
- data/tasks/release.rake +52 -0
- data/tasks/reversion.rake +8 -0
- metadata +105 -0
data/doc/LEGAL
ADDED
data/example/hello.nag
ADDED
data/example/morpher.nag
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Nagoro Pipe::Morph</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<?r list = [true, false, true] ?>
|
7
|
+
|
8
|
+
True items in list:
|
9
|
+
<div for="cond in list">
|
10
|
+
<h1 if="cond">#{cond}</h1>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
False items in list:
|
14
|
+
<div for="cond in list">
|
15
|
+
<h1 unless="cond">#{cond}</h1>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
Each item 3 times:
|
19
|
+
<div each="list">
|
20
|
+
<h1 times="3">#{_e}</h1>
|
21
|
+
</div>
|
22
|
+
</body>
|
23
|
+
</html>
|
data/lib/nagoro.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'nagoro/version'
|
4
|
+
require 'nagoro/template'
|
5
|
+
|
6
|
+
module Nagoro
|
7
|
+
autoload :Tidy, 'nagoro/tidy'
|
8
|
+
|
9
|
+
def self.compile(io, options = {})
|
10
|
+
Template.new.compile(io, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.render(obj, options = {})
|
14
|
+
compile(obj, options).result
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.tidy_render(obj, options = {})
|
18
|
+
compile(obj, options).tidy_result
|
19
|
+
end
|
20
|
+
|
21
|
+
class Error < StandardError; end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Nagoro
|
2
|
+
class Element
|
3
|
+
attr_accessor :content, :params
|
4
|
+
|
5
|
+
def initialize(content)
|
6
|
+
@content = content.strip
|
7
|
+
end
|
8
|
+
|
9
|
+
def render
|
10
|
+
@content
|
11
|
+
end
|
12
|
+
|
13
|
+
def params=(params = {})
|
14
|
+
params.each_pair do |key, value|
|
15
|
+
instance_variable_set("@#{key}", value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.inherited(klass)
|
20
|
+
Nagoro::Pipe::Element::ELEMENTS[klass.to_s] = klass
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# FileElement puts created elements into this module
|
25
|
+
module GeneratedElement
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.FileElement(file)
|
29
|
+
element = Class.new(Element){
|
30
|
+
define_method(:element_file){
|
31
|
+
file
|
32
|
+
}
|
33
|
+
|
34
|
+
def render
|
35
|
+
main = File.read(element_file)
|
36
|
+
@delim = "T" << rand(8 << 100).to_s
|
37
|
+
eval(%{<<#@delim\n#{main}\n#@delim})
|
38
|
+
end
|
39
|
+
}
|
40
|
+
name = File.basename(file, File.extname(file))
|
41
|
+
GeneratedElement::const_set(name, element)
|
42
|
+
Pipe::Element::ELEMENTS.delete_if{|k,v| v == element}
|
43
|
+
Pipe::Element::ELEMENTS[name] = element
|
44
|
+
element
|
45
|
+
end
|
46
|
+
end
|
data/lib/nagoro/pipe.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Nagoro
|
2
|
+
module Pipe
|
3
|
+
autoload :Base, 'nagoro/pipe/base'
|
4
|
+
autoload :Compile, 'nagoro/pipe/compile'
|
5
|
+
autoload :Element, 'nagoro/pipe/element'
|
6
|
+
autoload :Include, 'nagoro/pipe/include'
|
7
|
+
autoload :Instruction, 'nagoro/pipe/instruction'
|
8
|
+
autoload :Localization, 'nagoro/pipe/localization'
|
9
|
+
autoload :Morph, 'nagoro/pipe/morph'
|
10
|
+
autoload :Tidy, 'nagoro/pipe/tidy'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Nagoro
|
2
|
+
module Pipe
|
3
|
+
class Base
|
4
|
+
EMPTY_TAG = %w[ area base basefont br col frame hr
|
5
|
+
img input isindex link meta param ]
|
6
|
+
|
7
|
+
def initialize(io)
|
8
|
+
@body, @stack = [], []
|
9
|
+
@scanner = Scanner.new(io, self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def result
|
13
|
+
@scanner.stream
|
14
|
+
@body.join
|
15
|
+
end
|
16
|
+
|
17
|
+
def tag_start(tag, original_attrs, value_attrs)
|
18
|
+
case tag
|
19
|
+
when *EMPTY_TAG
|
20
|
+
append "#{tag_with(tag, original_attrs)} />"
|
21
|
+
else
|
22
|
+
append "#{tag_with(tag, original_attrs)}>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def tag_end(tag)
|
27
|
+
case tag
|
28
|
+
when *EMPTY_TAG
|
29
|
+
else
|
30
|
+
append "</#{tag}>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def text(string)
|
35
|
+
append(string)
|
36
|
+
end
|
37
|
+
|
38
|
+
def append(string)
|
39
|
+
@body << string
|
40
|
+
end
|
41
|
+
|
42
|
+
def instruction(name, instruction)
|
43
|
+
append "<?#{name} #{instruction.to_s.strip} ?>"
|
44
|
+
end
|
45
|
+
|
46
|
+
def tag_with(tag, hash)
|
47
|
+
"<#{tag}#{hash.map{|k,v| %( #{k}=#{v}) }.join}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def doctype(string)
|
51
|
+
string.strip!
|
52
|
+
append "<!DOCTYPE #{string}>"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Nagoro
|
2
|
+
module Pipe
|
3
|
+
class Compile < Base
|
4
|
+
def instruction(name, instruction)
|
5
|
+
case name
|
6
|
+
when 'r'
|
7
|
+
append("`;#{instruction}; _out_ << %Q`")
|
8
|
+
when 'ro'
|
9
|
+
append("`;_out_ << (#{instruction}); _out_ << %Q`")
|
10
|
+
when 'h'
|
11
|
+
append("`;_out_ << h(#{instruction}); _out_ << %Q`")
|
12
|
+
when 'end'
|
13
|
+
append("`;end; _out_ << %Q`")
|
14
|
+
else
|
15
|
+
append("<?#{name} #{instruction.strip} ?>")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def result
|
20
|
+
"_out_ = []; _out_ << %Q`#{super}`; _out_.join"
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile(template)
|
24
|
+
template = template.read if template.respond_to?(:read)
|
25
|
+
copy = template.gsub('`', '\\\\`')
|
26
|
+
compile!(copy)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Nagoro
|
2
|
+
def self.element(name, obj = nil, &block)
|
3
|
+
Pipe::Element::ELEMENTS[name] = obj || block
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.file_element(name, filename)
|
7
|
+
Pipe::Element::ELEMENTS[name] = FileElement(filename)
|
8
|
+
end
|
9
|
+
|
10
|
+
module Pipe
|
11
|
+
class Element < Base
|
12
|
+
ELEMENTS = {}
|
13
|
+
|
14
|
+
class ElementStruct < Struct.new(:tag, :attrs, :element, :content)
|
15
|
+
end
|
16
|
+
|
17
|
+
def tag_start(tag, original_attrs, value_attrs)
|
18
|
+
if element = ELEMENTS[tag]
|
19
|
+
@stack << ElementStruct.new(tag, value_attrs, element, [])
|
20
|
+
elsif tag =~ /^[A-Z]/
|
21
|
+
warn "Element: '<#{tag}>' not found."
|
22
|
+
super
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def tag_end(tag)
|
29
|
+
estruct = @stack.reverse.find{|e| e.tag == tag}
|
30
|
+
if estruct and estruct.tag == tag
|
31
|
+
attrs, element, content = estruct.values_at(1..3)
|
32
|
+
|
33
|
+
@stack.pop
|
34
|
+
|
35
|
+
if element.respond_to?(:call)
|
36
|
+
append element.call(content.join, attrs)
|
37
|
+
else
|
38
|
+
instance = element.new(content.join)
|
39
|
+
instance.params = translate_attrs(instance, estruct.attrs)
|
40
|
+
append instance.render
|
41
|
+
end
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def append(string)
|
48
|
+
if @stack.empty?
|
49
|
+
@body << string
|
50
|
+
else
|
51
|
+
@stack.last.content << string
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def translate_attrs(instance, attrs)
|
56
|
+
hash = {}
|
57
|
+
attrs.each do |key, value|
|
58
|
+
case value
|
59
|
+
when /^@/
|
60
|
+
hash[key] = value
|
61
|
+
else
|
62
|
+
hash[key] = value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
hash
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Nagoro
|
2
|
+
module Pipe
|
3
|
+
|
4
|
+
# Include is used to include the contents of another file.
|
5
|
+
# The file will not be changed or interpreted in any way by this pipe.
|
6
|
+
#
|
7
|
+
# If the tag contains anything the contents will be put after the included
|
8
|
+
# contents.
|
9
|
+
#
|
10
|
+
# Syntax:
|
11
|
+
# <include href="some_file.xhtml" />
|
12
|
+
# <include src="some_file.xhtml" />
|
13
|
+
|
14
|
+
class Include < Base
|
15
|
+
def tag_start(tag, original_attrs, value_attrs)
|
16
|
+
if tag == 'include'
|
17
|
+
filename = value_attrs['href'] || value_attrs.fetch('src')
|
18
|
+
append contents(filename)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def contents(file)
|
25
|
+
open(file){|o| o.read.strip }
|
26
|
+
rescue Errno::ENOENT, Errno::EISDIR => ex
|
27
|
+
warn ex.message
|
28
|
+
"<!-- #{ex} -->"
|
29
|
+
end
|
30
|
+
|
31
|
+
def tag_end(tag)
|
32
|
+
super unless tag == 'include'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Nagoro
|
2
|
+
module Pipe
|
3
|
+
|
4
|
+
# Instruction handles <??> instructions.
|
5
|
+
#
|
6
|
+
# It is based on a simple interpolation with String#% and one argument, the
|
7
|
+
# content of the instruction.
|
8
|
+
#
|
9
|
+
# If you want to add new instruction-formats, just add it to the
|
10
|
+
# Nagoro::Pipe::Instruction::INSTRUCTIONS Hash or use
|
11
|
+
# Nagoro::Pipe::Instruction::[] for convinience.
|
12
|
+
#
|
13
|
+
# The name of the instruction is matched with ===, so you can use regular
|
14
|
+
# expressions as keys as well.
|
15
|
+
#
|
16
|
+
# Syntax and results of available instructions:
|
17
|
+
# <?js alert('Hello, World!'); ?>
|
18
|
+
# # <script type="text/javascript"> alert('Hello, World!'); </script>
|
19
|
+
#
|
20
|
+
# <?js:src /js/jquery.js ?>
|
21
|
+
# # <script type="text/javascript" src="/js/jquery.js"></script>
|
22
|
+
#
|
23
|
+
# <?css body{ color: #f00; } ?>
|
24
|
+
# # <style type="text/css"> body{ color: #f00; } </style>
|
25
|
+
#
|
26
|
+
# <?css:src /css/coderay.css ?>
|
27
|
+
# # <style type="text/css" src="/css/coderay.css"></style>
|
28
|
+
#
|
29
|
+
# How to add new instructions:
|
30
|
+
# Nagoro::Pipe::Instruction['pre'] = '<pre>%s</pre>'
|
31
|
+
# Nagoro::Pipe::Instruction[/strip/] = '#{%(%s).strip}'
|
32
|
+
|
33
|
+
class Instruction < Base
|
34
|
+
INSTRUCTIONS = {
|
35
|
+
'js' => '<script type="text/javascript"> %s </script>',
|
36
|
+
'js:src' => '<script type="text/javascript" src="%s"></script>',
|
37
|
+
'css' => '<style type="text/css"> %s </style>',
|
38
|
+
'css:src' => '<style type="text/css" src="%s"></style>',
|
39
|
+
}
|
40
|
+
|
41
|
+
DEFAULT = "<?%s %s ?>"
|
42
|
+
|
43
|
+
def instruction(name, instruction)
|
44
|
+
instruction.to_s.strip!
|
45
|
+
|
46
|
+
if custom = INSTRUCTIONS[name]
|
47
|
+
@body << custom % instruction
|
48
|
+
else
|
49
|
+
@body << DEFAULT % [name, instruction]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def []=(key, value)
|
55
|
+
INSTRUCTIONS[key] = value
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](key)
|
59
|
+
INSTRUCTIONS[key]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
$KCODE = 'UTF-8'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
4
|
+
require 'ya2yaml'
|
5
|
+
|
6
|
+
module Nagoro
|
7
|
+
module Pipe
|
8
|
+
class Localization
|
9
|
+
attr_accessor :dict, :locale
|
10
|
+
|
11
|
+
CONFIG = {
|
12
|
+
:file => 'config/%s.yaml',
|
13
|
+
:locales => %w[ en ],
|
14
|
+
:default_language => 'en',
|
15
|
+
:mapping => {'en-us' => 'en', 'ja' => 'jp'},
|
16
|
+
:regex => /\[\[(.*?)\]\]/,
|
17
|
+
}
|
18
|
+
|
19
|
+
def self.[](conf = {})
|
20
|
+
CONFIG.merge!(conf)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
load_dictionary
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_dictionary
|
28
|
+
file = CONFIG[:file]
|
29
|
+
files = CONFIG[:locales].map{|l| [l, file % l]}
|
30
|
+
existing, missing = files.partition{|(l,f)| File.file?(f)}
|
31
|
+
FileUtils.mkdir_p(File.dirname(file)) unless missing.empty?
|
32
|
+
missing.each{|(l,f)| FileUtils.touch(f)}
|
33
|
+
|
34
|
+
@dict = {}
|
35
|
+
files.each do |locale, file|
|
36
|
+
@dict[locale] = YAML.load_file(file) || {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(template)
|
41
|
+
@template = template
|
42
|
+
@template.gsub!(CONFIG[:regex]) do |e|
|
43
|
+
localize($1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def localize(string)
|
48
|
+
@dict[locale][string]
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_html
|
52
|
+
@template
|
53
|
+
end
|
54
|
+
|
55
|
+
def reset
|
56
|
+
@template = ''
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|