excesselt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ coverage/*
5
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ excesselt (1.0.0)
5
+ builder (= 2.1.2)
6
+ nokogiri (= 1.4.3.1)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activesupport (3.0.1)
12
+ builder (2.1.2)
13
+ columnize (0.3.1)
14
+ diff-lcs (1.1.2)
15
+ gemcutter (0.6.1)
16
+ git (1.2.5)
17
+ jeweler (1.4.0)
18
+ gemcutter (>= 0.1.0)
19
+ git (>= 1.2.5)
20
+ rubyforge (>= 2.0.0)
21
+ json_pure (1.4.6)
22
+ linecache (0.43)
23
+ nokogiri (1.4.3.1)
24
+ rake (0.8.7)
25
+ rcov (0.9.9)
26
+ rspec (2.0.1)
27
+ rspec-core (~> 2.0.1)
28
+ rspec-expectations (~> 2.0.1)
29
+ rspec-mocks (~> 2.0.1)
30
+ rspec-core (2.0.1)
31
+ rspec-expectations (2.0.1)
32
+ diff-lcs (>= 1.1.2)
33
+ rspec-mocks (2.0.1)
34
+ rspec-core (~> 2.0.1)
35
+ rspec-expectations (~> 2.0.1)
36
+ ruby-debug (0.10.3)
37
+ columnize (>= 0.1)
38
+ ruby-debug-base (~> 0.10.3.0)
39
+ ruby-debug-base (0.10.3)
40
+ linecache (>= 0.3)
41
+ rubyforge (2.0.4)
42
+ json_pure (>= 1.1.7)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ activesupport
49
+ builder (= 2.1.2)
50
+ excesselt!
51
+ jeweler
52
+ nokogiri (= 1.4.3.1)
53
+ rake
54
+ rcov
55
+ rspec (> 2.0.0)
56
+ ruby-debug
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2010-10-29
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/PostInstall.txt ADDED
@@ -0,0 +1 @@
1
+ You're ready to get rid of that awful XSLT stuck in your codebase!
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Excesselt
2
+
3
+ http://github.com/DanielHeath/excesselt
4
+
5
+ ## DESCRIPTION:
6
+
7
+ Excesselt is a ruby library that I built because I hate XSLT.
8
+
9
+ I've extracted it from an app I built for my work at Lonely Planet.
10
+
11
+ Excesselt solves the same problem as XSLT does (that is, how can I transform this xml document into some other format).
12
+
13
+ ## FEATURES/PROBLEMS:
14
+
15
+ Nice syntax.
16
+ Testable, reusable xml transformation
17
+ Tested on REE 1.8.7 - TODO test on more platforms and update this section
18
+
19
+ ## SYNOPSIS:
20
+
21
+ class MyStylesheet < Excesselt::Stylesheet
22
+ def rules
23
+ render('parent > child') { builder.p(:style => "child_content" ) { child_content } }
24
+ render('parent') { builder.p(:style => "parent_content") { child_content } }
25
+ render('text()') { add element.to_xml.upcase }
26
+ end
27
+ end
28
+
29
+ MyStylesheet.transform <<-XML
30
+ <parent>
31
+ <child>Use Excesselt</child>
32
+ </parent>
33
+ XML
34
+ -> <p style="parent_content"><p style="child_content">USE EXCESSELT</p></p>
35
+
36
+ ## REQUIREMENTS:
37
+
38
+ * Nokogiri, Builder
39
+
40
+ ## INSTALL:
41
+
42
+ * gem install excesselt
43
+
44
+ ## LICENSE:
45
+
46
+ (The MIT License)
47
+
48
+ Copyright (c) 2010 Daniel Heath
49
+
50
+ Permission is hereby granted, free of charge, to any person obtaining
51
+ a copy of this software and associated documentation files (the
52
+ 'Software'), to deal in the Software without restriction, including
53
+ without limitation the rights to use, copy, modify, merge, publish,
54
+ distribute, sublicense, and/or sell copies of the Software, and to
55
+ permit persons to whom the Software is furnished to do so, subject to
56
+ the following conditions:
57
+
58
+ The above copyright notice and this permission notice shall be
59
+ included in all copies or substantial portions of the Software.
60
+
61
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
62
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
63
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
64
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
65
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
66
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
67
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ Bundler::GemHelper.install_tasks
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'jeweler'
12
+ require 'excesselt'
13
+ Jeweler::Tasks.new do |s|
14
+ s.platform = Gem::Platform::RUBY
15
+ s.name = 'excesselt'
16
+ s.version = Excesselt::VERSION
17
+ s.summary = 'Helps you to transform XML without using XSLT.'
18
+ s.description = 'I had a lot of XML transformation to do and the requirements kept changing, so I sat down and wrote something that was easy to modify.'
19
+
20
+ s.required_ruby_version = '>= 1.8.7'
21
+ s.required_rubygems_version = ">= 1.3.6"
22
+
23
+ s.author = 'Daniel Heath'
24
+ s.email = 'daniel.r.heath@gmail.com'
25
+ s.homepage = 'http://www.github.com/danielheath/excesselt'
26
+
27
+ # Would be good to take a stylesheet and an xml file as arguments for a binary.
28
+ # s.bindir = 'bin'
29
+ # s.executables = ['excesselt']
30
+ # s.default_executable = 'excesselt'
31
+
32
+ s.rdoc_options = ["--charset=UTF-8", "-mREADME.md"]
33
+
34
+ s.add_dependency('nokogiri', '1.4.3.1')
35
+ s.add_dependency('builder', '2.1.2')
36
+ s.add_development_dependency('activesupport')
37
+ s.add_development_dependency('rake')
38
+ s.add_development_dependency('rspec', '>2.0.0')
39
+ s.add_development_dependency('rcov')
40
+ s.add_development_dependency('ruby-debug')
41
+ s.add_development_dependency('jeweler')
42
+ end
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = Excesselt::VERSION
47
+ rdoc.main = "README.md"
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "<%= project_name %> #{version}"
50
+ rdoc.rdoc_files.include('README.md')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
53
+
54
+ Dir.glob('tasks/*').each {|rakefile| load rakefile }
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ Test on multiple rubies
2
+ Add binary
3
+ Add more examples
data/excesselt.gemspec ADDED
@@ -0,0 +1,102 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{excesselt}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Daniel Heath"]
12
+ s.date = %q{2010-11-08}
13
+ s.description = %q{I had a lot of XML transformation to do and the requirements kept changing, so I sat down and wrote something that was easy to modify.}
14
+ s.email = %q{daniel.r.heath@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md",
17
+ "TODO"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "History.txt",
24
+ "PostInstall.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "TODO",
28
+ "excesselt.gemspec",
29
+ "lib/excesselt.rb",
30
+ "lib/excesselt/element_wrapper.rb",
31
+ "lib/excesselt/rule.rb",
32
+ "lib/excesselt/stylesheet.rb",
33
+ "rdoc/classes/Excesselt.html",
34
+ "rdoc/classes/Excesselt/ElementWrapper.html",
35
+ "rdoc/classes/Excesselt/Rule.html",
36
+ "rdoc/classes/Excesselt/Stylesheet.html",
37
+ "rdoc/created.rid",
38
+ "rdoc/files/README_md.html",
39
+ "rdoc/files/lib/excesselt/element_wrapper_rb.html",
40
+ "rdoc/files/lib/excesselt/rule_rb.html",
41
+ "rdoc/files/lib/excesselt/stylesheet_rb.html",
42
+ "rdoc/files/lib/excesselt_rb.html",
43
+ "rdoc/fr_class_index.html",
44
+ "rdoc/fr_file_index.html",
45
+ "rdoc/fr_method_index.html",
46
+ "rdoc/index.html",
47
+ "rdoc/rdoc-style.css",
48
+ "script/console",
49
+ "script/destroy",
50
+ "script/generate",
51
+ "spec/excesselt_spec.rb",
52
+ "spec/spec_helper.rb",
53
+ "spec/support/matchers/dom_matcher.rb",
54
+ "tasks/rspec.rake"
55
+ ]
56
+ s.homepage = %q{http://www.github.com/danielheath/excesselt}
57
+ s.rdoc_options = ["--charset=UTF-8", "-mREADME.md"]
58
+ s.require_paths = ["lib"]
59
+ s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
60
+ s.rubygems_version = %q{1.3.7}
61
+ s.summary = %q{Helps you to transform XML without using XSLT.}
62
+ s.test_files = [
63
+ "spec/excesselt_spec.rb",
64
+ "spec/spec_helper.rb",
65
+ "spec/support/matchers/dom_matcher.rb"
66
+ ]
67
+
68
+ if s.respond_to? :specification_version then
69
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
70
+ s.specification_version = 3
71
+
72
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
73
+ s.add_runtime_dependency(%q<nokogiri>, ["= 1.4.3.1"])
74
+ s.add_runtime_dependency(%q<builder>, ["= 2.1.2"])
75
+ s.add_development_dependency(%q<activesupport>, [">= 0"])
76
+ s.add_development_dependency(%q<rake>, [">= 0"])
77
+ s.add_development_dependency(%q<rspec>, ["> 2.0.0"])
78
+ s.add_development_dependency(%q<rcov>, [">= 0"])
79
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
80
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
81
+ else
82
+ s.add_dependency(%q<nokogiri>, ["= 1.4.3.1"])
83
+ s.add_dependency(%q<builder>, ["= 2.1.2"])
84
+ s.add_dependency(%q<activesupport>, [">= 0"])
85
+ s.add_dependency(%q<rake>, [">= 0"])
86
+ s.add_dependency(%q<rspec>, ["> 2.0.0"])
87
+ s.add_dependency(%q<rcov>, [">= 0"])
88
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
89
+ s.add_dependency(%q<jeweler>, [">= 0"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<nokogiri>, ["= 1.4.3.1"])
93
+ s.add_dependency(%q<builder>, ["= 2.1.2"])
94
+ s.add_dependency(%q<activesupport>, [">= 0"])
95
+ s.add_dependency(%q<rake>, [">= 0"])
96
+ s.add_dependency(%q<rspec>, ["> 2.0.0"])
97
+ s.add_dependency(%q<rcov>, [">= 0"])
98
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
99
+ s.add_dependency(%q<jeweler>, [">= 0"])
100
+ end
101
+ end
102
+
@@ -0,0 +1,37 @@
1
+ module Excesselt
2
+ class ElementWrapper
3
+
4
+ attr_reader :element, :builder, :stylesheet
5
+
6
+ def initialize(stylesheet, element, builder)
7
+ @stylesheet = stylesheet
8
+ @element = element
9
+ @builder = builder
10
+ end
11
+
12
+ def child_content(selector=nil)
13
+ elements = selector ? @element.css(selector) : @element.children
14
+ elements.each do |child|
15
+ stylesheet.generate_element(child)
16
+ end
17
+ end
18
+
19
+ def method_missing(sym, *args)
20
+ begin
21
+ @element.send(sym, *args)
22
+ rescue Exception => e
23
+ raise e.exception("Error delegating method '#{sym}' to #{@element.class.name}: #{e.message}\n\n#{e.backtrace.join("\n")}")
24
+ end
25
+ end
26
+
27
+ def add(*content)
28
+ @builder << content.join('')
29
+ end
30
+
31
+ def error(string)
32
+ stylesheet.errors << string
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,45 @@
1
+ module Excesselt
2
+ class Rule
3
+
4
+ attr_reader :stylesheet, :element, :block, :selector
5
+
6
+ def initialize(stylesheet, selector, extensions, &block)
7
+ @stylesheet = stylesheet
8
+ @selector = selector
9
+ @extensions = extensions
10
+ @block = block
11
+ end
12
+
13
+ def matching_elements(document)
14
+ @selector_cache ||= {}
15
+ @selector_cache[document] ||= document.css(@selector)
16
+ end
17
+
18
+ def applies_to_element?
19
+ matching_elements(element.document).include? element
20
+ end
21
+
22
+ def matches?(element)
23
+ @element = element
24
+ if applies_to_element?
25
+ self # if it matches, nil otherwise
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ def generate(builder)
32
+ # Call the block in the elements context
33
+ wrapper = ElementWrapper.new(stylesheet, element, builder)
34
+ @extensions.each {|e| wrapper.extend e }
35
+ wrapper.instance_eval(&@block)
36
+ rescue Exception => e
37
+ if e.message =~ /With Included Modules/
38
+ raise e
39
+ else
40
+ raise e.class, "With Included Modules: #{@extensions.inspect}\n#{e.message}\n#{e.backtrace.join("\n")}"
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ require 'builder'
2
+ module Excesselt
3
+ class Stylesheet
4
+
5
+ attr_reader :builder, :errors
6
+
7
+ def self.transform(xml)
8
+ self.new.transform(xml)
9
+ end
10
+
11
+ def initialize
12
+ @builder = Builder::XmlMarkup.new
13
+ @helper_modules = []
14
+ @errors = []
15
+ end
16
+
17
+ def transform(xml)
18
+ generate_element(Nokogiri::XML(xml, nil, nil, Nokogiri::XML::ParseOptions.new).root)
19
+ end
20
+
21
+ def generate_element(element)
22
+ rule = rule_for(element)
23
+ raise "Attempted to generate #{self.name} with parents #{self.parents.inspect} but no rule was found." unless rule
24
+ rule.generate(builder)
25
+ end
26
+
27
+ private
28
+
29
+ def rule_for(element)
30
+ # Look up the rule that is used to render this.
31
+ # Should fold into stylesheet.rules (collection) .find(:matches?, element)
32
+ # TODO: Patch enumerable#find etc to take a plain symbol and some arguments?
33
+ rule = get_rules.find {|rule| rule.matches? element }
34
+ rule or raise "There is no style defined to handle element #{element.node.name} in this context (element.node.parents.inspect)"
35
+ end
36
+
37
+ def helper(*mods, &block)
38
+ @helper_modules.push(mods).flatten!
39
+ block.call
40
+ @helper_modules -= [mods].flatten
41
+ end
42
+
43
+ def render(selector, opts={}, &block)
44
+ raise "Neither a block nor a :with option were provided for '#{selector}'" unless (opts[:with] or block)
45
+
46
+ mappings << Rule.new(self, selector, @helper_modules) do
47
+ opts[:with] ? self.send(opts[:with]) : (instance_eval &block)
48
+ end
49
+ end
50
+
51
+ def mappings
52
+ @mappings ||= []
53
+ end
54
+
55
+ def get_rules
56
+ unless @rules_generated
57
+ rules # Generates the mappings
58
+ @rules_generated = true
59
+ end
60
+ mappings
61
+ end
62
+
63
+ end
64
+ end
data/lib/excesselt.rb ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ module Excesselt
4
+ VERSION = '1.0.0'
5
+ end
6
+
7
+ require 'excesselt/stylesheet'
8
+ require 'excesselt/rule'
9
+ require 'excesselt/element_wrapper'