rslt 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.travis.yml +10 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +53 -0
  4. data/History.txt +4 -0
  5. data/PostInstall.txt +1 -0
  6. data/README.md +67 -0
  7. data/Rakefile +55 -0
  8. data/TODO +3 -0
  9. data/lib/rslt/element_wrapper.rb +37 -0
  10. data/lib/rslt/rule.rb +45 -0
  11. data/lib/rslt/stylesheet.rb +94 -0
  12. data/lib/rslt.rb +9 -0
  13. data/rdoc/RSLT/ElementWrapper.html +357 -0
  14. data/rdoc/RSLT/Rule.html +379 -0
  15. data/rdoc/RSLT/Stylesheet.html +311 -0
  16. data/rdoc/RSLT.html +132 -0
  17. data/rdoc/created.rid +6 -0
  18. data/rdoc/images/add.png +0 -0
  19. data/rdoc/images/brick.png +0 -0
  20. data/rdoc/images/brick_link.png +0 -0
  21. data/rdoc/images/bug.png +0 -0
  22. data/rdoc/images/bullet_black.png +0 -0
  23. data/rdoc/images/bullet_toggle_minus.png +0 -0
  24. data/rdoc/images/bullet_toggle_plus.png +0 -0
  25. data/rdoc/images/date.png +0 -0
  26. data/rdoc/images/delete.png +0 -0
  27. data/rdoc/images/find.png +0 -0
  28. data/rdoc/images/loadingAnimation.gif +0 -0
  29. data/rdoc/images/macFFBgHack.png +0 -0
  30. data/rdoc/images/package.png +0 -0
  31. data/rdoc/images/page_green.png +0 -0
  32. data/rdoc/images/page_white_text.png +0 -0
  33. data/rdoc/images/page_white_width.png +0 -0
  34. data/rdoc/images/plugin.png +0 -0
  35. data/rdoc/images/ruby.png +0 -0
  36. data/rdoc/images/tag_blue.png +0 -0
  37. data/rdoc/images/tag_green.png +0 -0
  38. data/rdoc/images/transparent.png +0 -0
  39. data/rdoc/images/wrench.png +0 -0
  40. data/rdoc/images/wrench_orange.png +0 -0
  41. data/rdoc/images/zoom.png +0 -0
  42. data/rdoc/index.html +75 -0
  43. data/rdoc/js/darkfish.js +153 -0
  44. data/rdoc/js/jquery.js +18 -0
  45. data/rdoc/js/navigation.js +142 -0
  46. data/rdoc/js/search.js +94 -0
  47. data/rdoc/js/search_index.js +1 -0
  48. data/rdoc/js/searcher.js +228 -0
  49. data/rdoc/rdoc.css +543 -0
  50. data/rdoc/table_of_contents.html +83 -0
  51. data/rslt.gemspec +150 -0
  52. data/script/console +10 -0
  53. data/script/destroy +14 -0
  54. data/script/generate +14 -0
  55. data/spec/element_wrapper_spec.rb +22 -0
  56. data/spec/excesselt_spec.rb +112 -0
  57. data/spec/spec_helper.rb +6 -0
  58. data/spec/support/matchers/dom_matcher.rb +45 -0
  59. data/tasks/rspec.rake +13 -0
  60. metadata +307 -0
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'nokogiri'
4
+ gem 'builder'
5
+
6
+ group :development do
7
+ gem 'activesupport'
8
+ gem 'rake'
9
+ gem 'i18n'
10
+ gem 'rspec', '>2.0.0'
11
+ gem 'rcov', :platform => 'ruby_18'
12
+ gem 'ruby-debug', :platform => 'ruby_18'
13
+ gem 'jeweler'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,53 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.3)
5
+ i18n (~> 0.6)
6
+ multi_json (~> 1.0)
7
+ builder (3.0.0)
8
+ columnize (0.3.6)
9
+ diff-lcs (1.1.3)
10
+ git (1.2.5)
11
+ i18n (0.6.0)
12
+ jeweler (1.8.3)
13
+ bundler (~> 1.0)
14
+ git (>= 1.2.5)
15
+ rake
16
+ rdoc
17
+ json (1.6.6)
18
+ linecache (0.46)
19
+ rbx-require-relative (> 0.0.4)
20
+ multi_json (1.2.0)
21
+ nokogiri (1.5.2)
22
+ rake (0.9.2.2)
23
+ rbx-require-relative (0.0.9)
24
+ rcov (1.0.0)
25
+ rdoc (3.12)
26
+ json (~> 1.4)
27
+ rspec (2.9.0)
28
+ rspec-core (~> 2.9.0)
29
+ rspec-expectations (~> 2.9.0)
30
+ rspec-mocks (~> 2.9.0)
31
+ rspec-core (2.9.0)
32
+ rspec-expectations (2.9.1)
33
+ diff-lcs (~> 1.1.3)
34
+ rspec-mocks (2.9.0)
35
+ ruby-debug (0.10.4)
36
+ columnize (>= 0.1)
37
+ ruby-debug-base (~> 0.10.4.0)
38
+ ruby-debug-base (0.10.4)
39
+ linecache (>= 0.3)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ activesupport
46
+ builder
47
+ i18n
48
+ jeweler
49
+ nokogiri
50
+ rake
51
+ rcov
52
+ rspec (> 2.0.0)
53
+ 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
+ # RSLT
2
+
3
+ http://github.com/DanielHeath/rslt
4
+
5
+ ## DESCRIPTION:
6
+
7
+ RSLT 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
+ RSLT 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 and MRI 1.9.3
18
+
19
+ ## SYNOPSIS:
20
+
21
+ class MyStylesheet < RSLT::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 RSLT</child>
32
+ </parent>
33
+ XML
34
+ -> <p style="parent_content"><p style="child_content">USE RSLT</p></p>
35
+
36
+ ## REQUIREMENTS:
37
+
38
+ * Nokogiri, Builder
39
+
40
+ ## INSTALL:
41
+
42
+ * gem install rslt
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,55 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ include Rake::DSL
5
+ Bundler::GemHelper.install_tasks
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'jeweler'
12
+ require './lib/rslt'
13
+ Jeweler::Tasks.new do |s|
14
+ s.platform = Gem::Platform::RUBY
15
+ s.name = 'rslt'
16
+ s.version = RSLT::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/rslt'
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 = ['rslt']
30
+ # s.default_executable = 'rslt'
31
+
32
+ s.rdoc_options = ["--charset=UTF-8", "-mREADME.md"]
33
+
34
+ s.add_dependency('nokogiri', '~>1')
35
+ s.add_dependency('builder', '>2')
36
+ s.add_development_dependency('activesupport')
37
+ s.add_development_dependency('rake')
38
+ s.add_development_dependency('i18n')
39
+ s.add_development_dependency('rspec', '>2.0.0')
40
+ s.add_development_dependency('rcov')
41
+ s.add_development_dependency('ruby-debug')
42
+ s.add_development_dependency('jeweler')
43
+ end
44
+
45
+ require 'rdoc/task'
46
+ RDoc::Task.new do |rdoc|
47
+ version = RSLT::VERSION
48
+ rdoc.main = "README.md"
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "<%= project_name %> #{version}"
51
+ rdoc.rdoc_files.include('README.md')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
54
+
55
+ 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
@@ -0,0 +1,37 @@
1
+ module RSLT
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
data/lib/rslt/rule.rb ADDED
@@ -0,0 +1,45 @@
1
+ module RSLT
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 selector '.*' and included modules/
38
+ raise e
39
+ else
40
+ raise e.class, "With selector '#{selector}' and included modules: #{@extensions.inspect}\n#{e.message}\n#{e.backtrace.join("\n")}"
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,94 @@
1
+ require 'active_support/core_ext/hash/except' rescue nil
2
+ require 'builder'
3
+
4
+ module RSLT
5
+ class Stylesheet
6
+
7
+ attr_reader :builder, :errors
8
+
9
+ def self.transform(xml)
10
+ self.new.transform(xml)
11
+ end
12
+
13
+ def initialize(options={})
14
+ @builder = options[:builder] || Builder::XmlMarkup.new
15
+ @helper_modules = options[:helper_modules] || []
16
+ @errors = options[:errors] || []
17
+ @within = []
18
+ end
19
+
20
+ # Pass in a string or a Nokogiri Node or Document.
21
+ def transform(xml)
22
+ xml = xml.root if xml.is_a? Nokogiri::XML::Document
23
+ xml = Nokogiri::XML(xml, nil, nil, Nokogiri::XML::ParseOptions.new).root unless xml.is_a? Nokogiri::XML::Node
24
+ generate_element(xml)
25
+ end
26
+
27
+ def generate_element(element)
28
+ return '' if element.instance_of? Nokogiri::XML::Comment
29
+ rule = rule_for(element)
30
+ raise "Attempted to generate #{self.name} with parents #{self.parents.inspect} but no rule was found." unless rule
31
+ rule.generate(builder)
32
+ end
33
+
34
+ private
35
+
36
+ def rule_for(element)
37
+ # Look up the rule that is used to render this.
38
+ # Should fold into stylesheet.rules (collection) .find(:matches?, element)
39
+ # TODO: Patch enumerable#find etc to take a plain symbol and some arguments?
40
+ rule = get_rules.find {|rule| rule.matches? element }
41
+
42
+ rule or raise [
43
+ "There is no style defined to handle this element.",
44
+ "CSS Path: '#{element.css_path}'",
45
+ "Xpath: '#{element.path}'",
46
+ "Context: '#{element.ancestors.map(&:name).reverse.join(", ")}'"
47
+ ].join("\n")
48
+ end
49
+
50
+ def helper(*mods, &block)
51
+ @helper_modules.push(mods).flatten!
52
+ block.call
53
+ @helper_modules -= [mods].flatten
54
+ end
55
+
56
+ def within(selector)
57
+ @within.push(selector)
58
+ yield
59
+ @within.pop
60
+ end
61
+
62
+ def selector_for_current_within
63
+ @within.map {|e| e + ' '}.join('')
64
+ end
65
+
66
+ def render(selector, opts={}, &block)
67
+ raise "Neither a block nor a :with option were provided for '#{selector}'" unless (opts[:with] or block)
68
+ mappings << Rule.new(self, selector_for_current_within + selector, @helper_modules) do
69
+ if opts[:with]
70
+ if method(opts[:with]).arity == 0
71
+ self.send(opts[:with])
72
+ else
73
+ self.send(opts[:with], opts.except(:with))
74
+ end
75
+ else
76
+ instance_eval &block
77
+ end
78
+ end
79
+ end
80
+
81
+ def mappings
82
+ @mappings ||= []
83
+ end
84
+
85
+ def get_rules
86
+ unless @rules_generated
87
+ rules # Generates the mappings
88
+ @rules_generated = true
89
+ end
90
+ mappings
91
+ end
92
+
93
+ end
94
+ end
data/lib/rslt.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 RSLT
4
+ VERSION = '1.1.8'
5
+ end
6
+
7
+ require 'rslt/stylesheet'
8
+ require 'rslt/rule'
9
+ require 'rslt/element_wrapper'