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.
Files changed (52) hide show
  1. data/CHANGELOG +446 -0
  2. data/MANIFEST +51 -0
  3. data/README.markdown +189 -0
  4. data/Rakefile +29 -0
  5. data/bin/nagoro +83 -0
  6. data/doc/COPYING +56 -0
  7. data/doc/GPL +340 -0
  8. data/doc/LEGAL +2 -0
  9. data/example/element/Html.nage +8 -0
  10. data/example/hello.nag +3 -0
  11. data/example/morpher.nag +23 -0
  12. data/lib/nagoro.rb +22 -0
  13. data/lib/nagoro/binding.rb +8 -0
  14. data/lib/nagoro/element.rb +46 -0
  15. data/lib/nagoro/pipe.rb +12 -0
  16. data/lib/nagoro/pipe/base.rb +56 -0
  17. data/lib/nagoro/pipe/compile.rb +30 -0
  18. data/lib/nagoro/pipe/element.rb +70 -0
  19. data/lib/nagoro/pipe/include.rb +36 -0
  20. data/lib/nagoro/pipe/instruction.rb +64 -0
  21. data/lib/nagoro/pipe/localization.rb +60 -0
  22. data/lib/nagoro/pipe/morph.rb +95 -0
  23. data/lib/nagoro/pipe/tidy.rb +49 -0
  24. data/lib/nagoro/scanner.rb +97 -0
  25. data/lib/nagoro/template.rb +89 -0
  26. data/lib/nagoro/tidy.rb +49 -0
  27. data/lib/nagoro/version.rb +3 -0
  28. data/nagoro.gemspec +28 -0
  29. data/spec/core_extensions.rb +33 -0
  30. data/spec/example/hello.rb +13 -0
  31. data/spec/helper.rb +19 -0
  32. data/spec/nagoro/listener/base.rb +19 -0
  33. data/spec/nagoro/pipe/compile.rb +31 -0
  34. data/spec/nagoro/pipe/element.rb +46 -0
  35. data/spec/nagoro/pipe/include.rb +17 -0
  36. data/spec/nagoro/pipe/instruction.rb +32 -0
  37. data/spec/nagoro/pipe/morph.rb +47 -0
  38. data/spec/nagoro/pipe/tidy.rb +23 -0
  39. data/spec/nagoro/template.rb +105 -0
  40. data/spec/nagoro/template/full.nag +26 -0
  41. data/spec/nagoro/template/hello.nag +1 -0
  42. data/tasks/bacon.rake +49 -0
  43. data/tasks/changelog.rake +18 -0
  44. data/tasks/gem.rake +22 -0
  45. data/tasks/gem_installer.rake +76 -0
  46. data/tasks/grancher.rake +12 -0
  47. data/tasks/install_dependencies.rake +6 -0
  48. data/tasks/manifest.rake +4 -0
  49. data/tasks/rcov.rake +19 -0
  50. data/tasks/release.rake +52 -0
  51. data/tasks/reversion.rake +8 -0
  52. metadata +105 -0
@@ -0,0 +1,95 @@
1
+ module Nagoro
2
+ module Pipe
3
+ # Morph is a search and replace system based on parameters of opening
4
+ # tags.
5
+ #
6
+ # Available morphs are at the time of writing:
7
+ # each, filter, foreach, if, times, unless
8
+ #
9
+ # Please take care not to use multiple morphable parameters in one tag, due
10
+ # to restrictions of the implementation of REXML and libxml there is no way
11
+ # to find out which one came first and so the order would get messed up,
12
+ # leading to unpredictable results.
13
+ # You can, however, use any number of other parameter together with morphs.
14
+ #
15
+ # Example for each predefined morph:
16
+ # <a if="@address" href="#@address">#@address</a>
17
+ # # <?r if @address ?>
18
+ # # <a href="#@address">#@address</a>
19
+ # # <?r end ?>
20
+ #
21
+ # <a unless="logged_in?" href="/login">Login to your account</a>
22
+ # # <?r unless logged_in? ?>
23
+ # # <a href="/login">Login to your account
24
+ # # <?r end ?>
25
+ #
26
+ # <div foreach="tag in @tags" class="tag">#{tag}</div>
27
+ # # <?r for tag in @tags ?>
28
+ # # <div class="tag">#{tag}</div>
29
+ # # <?r end ?>
30
+ #
31
+ # <p filter="emoticonize">Hello there :)</p>
32
+ # # <p>#{emoticonize(%<Hello there :)>)}</p>
33
+ #
34
+ # <div times="10" class="shout">I am innocent!</div>
35
+ # # <?r 10.times do |_t| ?>
36
+ # # <div class="shout">I am innocent!</div>
37
+ # # <?r end ?>
38
+ #
39
+ # <div each="@users">#{_e}</div>
40
+ # # <?r @users.each do |_e| ?>
41
+ # # <div>#{_e}</div>
42
+ # # <?r end ?>
43
+ #
44
+ # How to add new morphs:
45
+ # Nagoro::Pipe::Morph['until'] = [
46
+ # '<?r %morph %expression ?>', '<?r end ?>' ]
47
+
48
+ class Morph < Base
49
+ MORPHS = {
50
+ 'each' => [ '<?r %expression.%morph do |_e| ?>', '<?r end ?>' ],
51
+ 'times' => [ '<?r %expression.%morph do |_t| ?>', '<?r end ?>' ],
52
+ 'filter' => [ '<?o %expression(%<', '>) ?>' ],
53
+ 'if' => [ '<?r %morph %expression ?>', '<?r end ?>' ],
54
+ 'unless' => [ '<?r %morph %expression ?>', '<?r end ?>' ],
55
+ 'foreach' => [ '<?r for %expression ?>', '<?r end ?>' ],
56
+ }
57
+
58
+ def tag_start(tag, original_attrs, value_attrs)
59
+ morphs = value_attrs.keys & MORPHS.keys
60
+ return super if morphs.empty?
61
+
62
+ if morphs.size > 1
63
+ raise "Cannot transform multiple morphs: #{value_attrs.inspect} in <#{tag}>"
64
+ elsif morphs.size == 1
65
+ morph = morphs.first
66
+ value = value_attrs.delete(morph)
67
+ original_attrs.delete(morph)
68
+ open = MORPHS[morph][0]
69
+
70
+ append open.gsub(/%morph/, morph).gsub(/%expression/, value)
71
+ @stack << [tag, morph]
72
+ super
73
+ end
74
+ end
75
+
76
+ def tag_end(tag)
77
+ super
78
+ prev, morph = @stack.last
79
+ return unless prev == tag
80
+ append(MORPHS[morph][1])
81
+ @stack.pop
82
+ end
83
+
84
+ class << self
85
+ def []=(key, value)
86
+ MORPHS[key] = value
87
+ end
88
+
89
+ def [](key)
90
+ MORPHS[key]
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,49 @@
1
+ require 'open3'
2
+
3
+ module Nagoro
4
+ module Pipe
5
+ # Small wrapper that pipes the template through tidy.
6
+ #
7
+ # For configuration see the {Tidy::FLAGS}, it's an array of arguments
8
+ # passed to the tidy command.
9
+ #
10
+ # We are not using the ruby tidy library to avoid memory-leaks and
11
+ # dependencies. Please make sure you do have a binary called `tidy` in your
12
+ # `PATH` before using this library.
13
+ #
14
+ # This pipe relies on open3 from the standard library.
15
+ #
16
+ # == Regarding Windows
17
+ #
18
+ # There have been numerous reports that open3 doesn't work on Windows,
19
+ # since it relies on Kernel#fork, as syscall that is not supported on that
20
+ # platform.
21
+ #
22
+ # Two possible solutions exist:
23
+ # * There is a win32-popen implementation in RAA
24
+ # * Kernel#fork works in cygwin.
25
+ #
26
+ # I don't have a copy of windows, so it's hard for me to try either of
27
+ # these options.
28
+ # Please send me a patch if you find a cross-platform way of implementing
29
+ # this functionality.
30
+ class Tidy < Base
31
+ # Possible flags can be found in `tidy -help` and `tidy -help-config`
32
+ FLAGS = %w[-utf8 -asxhtml -quiet -indent --tidy-mark no]
33
+
34
+ def result
35
+ @scanner.stream
36
+ tidy(@body.join)
37
+ end
38
+
39
+ # Pipe given +string+ through the tidy binary.
40
+ #
41
+ # @param [#to_s] string
42
+ # @return [String]
43
+ # @author manveru
44
+ def tidy(string)
45
+ Nagoro::Tidy.tidy(string, FLAGS)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,97 @@
1
+ require 'strscan'
2
+
3
+ module Nagoro
4
+ class Scanner < StringScanner
5
+ TEXT = /[^<>]+/m
6
+ DOCTYPE = /<!DOCTYPE([^>]+)>/m
7
+
8
+ TAG_START = /<([^\s>]+)/
9
+ TAG_END = /<\/([^>]*)>/
10
+ TAG_OPEN_END = /\s*>/
11
+ TAG_CLOSING_END = /\s*\/>/
12
+ TAG_PARAMETER = /\s*([^\s]*)=((['"])(.*?)\3)/um
13
+
14
+ INSTRUCTION_START = /<\?(\S+)/
15
+ INSTRUCTION_END = /(.*?)(\?>)/um
16
+
17
+ RUBY_INTERP_START = /\s*#\{/m
18
+ RUBY_INTERP_TEXT = /[^\{\}]+/m
19
+ RUBY_INTERP_NEST = /\{[^\}]*\}/m
20
+ RUBY_INTERP_END = /(?=\})/
21
+
22
+ COMMENT = /<!--.*?-->/m
23
+
24
+ def initialize(string, callback)
25
+ @callback = callback
26
+ super(string)
27
+ end
28
+
29
+ def stream
30
+ until eos?
31
+ pos = self.pos
32
+ run
33
+ raise(Stuck, "Scanner didn't move: %p" % self) if pos == self.pos
34
+ end
35
+ end
36
+
37
+ def run
38
+ if scan(DOCTYPE ); doctype(self[1])
39
+ elsif scan(INSTRUCTION_START); instruction(self[1])
40
+ elsif scan(COMMENT ); text(matched)
41
+ elsif scan(TAG_END ); tag_end(self[1])
42
+ elsif scan(RUBY_INTERP_START); ruby_interp(matched)
43
+ elsif scan(TAG_START ); tag_start(self[1])
44
+ elsif scan(TEXT ); text(matched)
45
+ end
46
+ end
47
+
48
+ def instruction(name)
49
+ scan(INSTRUCTION_END)
50
+ @callback.instruction(name, self[1])
51
+ end
52
+
53
+ def ruby_interp(string)
54
+ done = false
55
+
56
+ until done
57
+ if scan(RUBY_INTERP_TEXT)
58
+ string << matched
59
+ elsif scan(RUBY_INTERP_NEST)
60
+ string << matched
61
+ elsif scan(RUBY_INTERP_END)
62
+ done = true
63
+ end
64
+ end
65
+
66
+ @callback.text(string)
67
+ end
68
+
69
+ def tag_start(name)
70
+ original_attrs = {}
71
+ value_attrs = {}
72
+
73
+ while scan(TAG_PARAMETER)
74
+ original_attrs[self[1]] = self[2] # <a href="foo"> gives 'href'=>'"foo"'
75
+ value_attrs[ self[1]] = self[4] # <a href="foo"> gives 'href'=>'foo'
76
+ end
77
+
78
+ @callback.tag_start(name, original_attrs, value_attrs)
79
+ return @callback.tag_end(name) if scan(TAG_CLOSING_END)
80
+ scan(TAG_OPEN_END)
81
+ end
82
+
83
+ def tag_end(name)
84
+ @callback.tag_end(name)
85
+ end
86
+
87
+ def text(string)
88
+ @callback.text(string)
89
+ end
90
+
91
+ def doctype(string)
92
+ @callback.doctype(string)
93
+ end
94
+
95
+ class Stuck < Error; end
96
+ end
97
+ end
@@ -0,0 +1,89 @@
1
+ require 'nagoro/scanner'
2
+ require 'nagoro/binding'
3
+ require 'nagoro/element'
4
+ require 'nagoro/pipe'
5
+
6
+ module Nagoro
7
+ DEFAULT_PIPES = [ Pipe::Element, Pipe::Morph, Pipe::Include,
8
+ Pipe::Instruction, Pipe::Compile ]
9
+ DEFAULT_FILE = '<nagoro eval>'
10
+ DEFAULT_LINE = 1
11
+
12
+ class Template
13
+ def self.[](*pipes)
14
+ new(:pipes => pipes.flatten)
15
+ end
16
+
17
+ attr_accessor :binding, :file, :pipes, :compiled
18
+
19
+ def initialize(options = {})
20
+ parse_option(options)
21
+ @compiled = false
22
+ end
23
+
24
+ def parse_option(options = {})
25
+ @binding = options.fetch(:binding, BindingProvider.binding)
26
+ @file = options[:filename] || DEFAULT_FILE
27
+ @line = options[:line] || DEFAULT_LINE
28
+ @pipes = options[:pipes] || DEFAULT_PIPES
29
+ @variables = options[:variables]
30
+ end
31
+
32
+ def compile(io, options = {})
33
+ parse_option(options) unless options.empty?
34
+
35
+ case io
36
+ when String
37
+ io = File.read(io) if io.size < 1024 and File.file?(io)
38
+ @compiled = pipeline(io)
39
+ when StringIO, IO
40
+ @compiled = pipeline(io.read)
41
+ else
42
+ raise(ArgumentError, "Cannot compile %p" % io)
43
+ end
44
+
45
+ return self
46
+ end
47
+
48
+ # use inject?
49
+ def pipeline(io)
50
+ @pipes.each do |pipe|
51
+ if pipe.respond_to?(:new)
52
+ io = pipe.new(io).result
53
+ else
54
+ io = Pipe.const_get(pipe).new(io).result
55
+ end
56
+ end
57
+
58
+ return io
59
+ rescue Scanner::Stuck => exception
60
+ exception.message << " In: #{@file}"
61
+ puts exception
62
+ ''
63
+ end
64
+
65
+ def result(options = {})
66
+ parse_option(options) unless options.empty?
67
+
68
+ if vars = @variables
69
+ obj = eval('self', @binding)
70
+ obj.instance_variable_set('@_nagoro_ivs', vars)
71
+ eval(%q(
72
+ @_nagoro_ivs.each{|key, value| instance_variable_set("@#{key}", value) }
73
+ ), @binding)
74
+ end
75
+
76
+ eval(@compiled, @binding, @file, @line).strip
77
+ end
78
+
79
+ def tidy_result(options = {})
80
+ final = result(options)
81
+ flags = options.fetch(:flags, Tidy::FLAGS)
82
+ Tidy.tidy(final, flags).strip
83
+ end
84
+
85
+ def render(io, options = {})
86
+ compile(io, options).result
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,49 @@
1
+ require 'open3'
2
+
3
+ module Nagoro
4
+ # Small wrapper that pipes the template through tidy.
5
+ #
6
+ # For configuration see the {Tidy::FLAGS}, it's an array of arguments
7
+ # passed to the tidy command.
8
+ #
9
+ # We are not using the ruby tidy library to avoid memory-leaks and
10
+ # dependencies. Please make sure you do have a binary called `tidy` in your
11
+ # `PATH` before using this library.
12
+ #
13
+ # This pipe relies on open3 from the standard library.
14
+ #
15
+ # == Regarding Windows
16
+ #
17
+ # There have been numerous reports that open3 doesn't work on Windows,
18
+ # since it relies on Kernel#fork, as syscall that is not supported on that
19
+ # platform.
20
+ #
21
+ # Two possible solutions exist:
22
+ # * There is a win32-popen implementation in RAA
23
+ # * Kernel#fork works in cygwin.
24
+ #
25
+ # I don't have a copy of windows, so it's hard for me to try either of
26
+ # these options.
27
+ # Please send me a patch if you find a cross-platform way of implementing
28
+ # this functionality.
29
+
30
+ module Tidy
31
+ # Possible flags can be found in `tidy -help` and `tidy -help-config`
32
+ FLAGS = %w[-utf8 -asxhtml -quiet -indent --tidy-mark no]
33
+
34
+ module_function
35
+
36
+ # Pipe given +string+ through the tidy binary.
37
+ #
38
+ # @param [#to_s] string
39
+ # @return [String]
40
+ # @author manveru
41
+ def tidy(string, flags = FLAGS)
42
+ Open3.popen3('tidy', *flags) do |stdin, stdout, stderr|
43
+ stdin.puts(string)
44
+ stdin.close
45
+ return stdout.read
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module Nagoro
2
+ VERSION = "2009.05"
3
+ end
data/nagoro.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{nagoro}
5
+ s.version = "2009.05"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Michael 'manveru' Fellinger"]
9
+ s.date = %q{2009-05-07}
10
+ s.description = %q{An extendible and fast templating engine in pure ruby.}
11
+ s.email = %q{m.fellinger@gmail.com}
12
+ s.files = ["CHANGELOG", "MANIFEST", "README.markdown", "Rakefile", "bin/nagoro", "doc/COPYING", "doc/GPL", "doc/LEGAL", "example/element/Html.nage", "example/hello.nag", "example/morpher.nag", "lib/nagoro.rb", "lib/nagoro/binding.rb", "lib/nagoro/element.rb", "lib/nagoro/pipe.rb", "lib/nagoro/pipe/base.rb", "lib/nagoro/pipe/compile.rb", "lib/nagoro/pipe/element.rb", "lib/nagoro/pipe/include.rb", "lib/nagoro/pipe/instruction.rb", "lib/nagoro/pipe/localization.rb", "lib/nagoro/pipe/morph.rb", "lib/nagoro/pipe/tidy.rb", "lib/nagoro/scanner.rb", "lib/nagoro/template.rb", "lib/nagoro/tidy.rb", "lib/nagoro/version.rb", "nagoro.gemspec", "spec/core_extensions.rb", "spec/example/hello.rb", "spec/helper.rb", "spec/nagoro/listener/base.rb", "spec/nagoro/pipe/compile.rb", "spec/nagoro/pipe/element.rb", "spec/nagoro/pipe/include.rb", "spec/nagoro/pipe/instruction.rb", "spec/nagoro/pipe/morph.rb", "spec/nagoro/pipe/tidy.rb", "spec/nagoro/template.rb", "spec/nagoro/template/full.nag", "spec/nagoro/template/hello.nag", "tasks/bacon.rake", "tasks/changelog.rake", "tasks/gem.rake", "tasks/gem_installer.rake", "tasks/grancher.rake", "tasks/install_dependencies.rake", "tasks/manifest.rake", "tasks/rcov.rake", "tasks/release.rake", "tasks/reversion.rake"]
13
+ s.has_rdoc = true
14
+ s.homepage = %q{http://github.com/manveru/nagoro}
15
+ s.require_paths = ["lib"]
16
+ s.rubygems_version = %q{1.3.2}
17
+ s.summary = %q{An extendible and fast templating engine in pure ruby.}
18
+
19
+ if s.respond_to? :specification_version then
20
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
21
+ s.specification_version = 3
22
+
23
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
24
+ else
25
+ end
26
+ else
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ # Extensions for Kernel
2
+
3
+ module Kernel
4
+ # This is similar to +__FILE__+ and +__LINE__+, and returns a String
5
+ # representing the directory of the current file is.
6
+ # Unlike +__FILE__+ the path returned is absolute.
7
+ #
8
+ # This method is convenience for the
9
+ # File.expand_path(File.dirname(__FILE__))
10
+ # idiom.
11
+ #
12
+ unless defined?(__DIR__)
13
+ def __DIR__()
14
+ filename = caller[0][/(.*?):/, 1]
15
+ File.expand_path(File.dirname(filename))
16
+ end
17
+ end
18
+ end
19
+
20
+ # Extensions for String
21
+
22
+ class String
23
+
24
+ # A convenient way to do File.join
25
+ #
26
+ # Example:
27
+ # 'a' / 'b' # -> 'a/b'
28
+ # File.dirname(__FILE__) / 'bar' # -> "ramaze/snippets/string/bar"
29
+
30
+ def / obj
31
+ File.join(self, obj.to_s)
32
+ end
33
+ end