nagoro 2009.05
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/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
|