dress 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Howard Yeh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,145 @@
1
+ h1. XSLT(not!) with Elegant Dress
2
+
3
+ You'd swear XSLT is a joke, but it's not. It's
4
+ cruelty. XSLT is like this: it's good to rid the
5
+ city of this horrible vermin infestation-- Let's
6
+ nuke the city. It's an out-of-proportion
7
+ insanity. It's Turing-complete, and it's XML.
8
+
9
+ Hmm.
10
+
11
+ But what it is, is just a set of transformations
12
+ on fragments of your XML document. The beauty of
13
+ XSLT (they say), is that XSLT itself is XML, so
14
+ you can (in principle) use XSLT to transform
15
+ itself.
16
+
17
+ Hmm.
18
+
19
+ Oh I know, how about let's use Ruby?
20
+
21
+ Dress is an XML transformation library built on
22
+ top of Nokogiri. Nokogiri is awesome. Dress is
23
+ built on top of Nokogiri...
24
+
25
+ By syllogism, Dress is awesome.
26
+
27
+
28
+ h1. Transformation Elegant Dress
29
+
30
+ Honestly, there's not much to it. A @Dress@ is
31
+ just a sequence of transformations you perform on
32
+ a DOM tree. Unlike XSLT though, the
33
+ transformations are performed directly on the DOM
34
+ tree. That is to say, they are destructive.
35
+
36
+ This is a simple dress,
37
+
38
+ require 'dress'
39
+ doc = "<my><brain></brain></my>"
40
+ dress = Dress {
41
+ match("brain") do
42
+ set("size","pea")
43
+ end
44
+ }
45
+ result = dress.on(doc)
46
+ puts result.to_s
47
+ # <my><brain "size"="pea"></brain></my>
48
+
49
+
50
+ We can have more matchers,
51
+
52
+ dress = Dress {
53
+ match("brain") do
54
+ set("size","pea")
55
+ end
56
+ match("my") do
57
+ each { |e| e.name = "homer"}
58
+ end
59
+ }
60
+ result = dress.on(doc)
61
+ puts result.to_s
62
+ # <homer><brain "size"="pea"></brain></homer>
63
+
64
+
65
+ Note that match always yield a
66
+ Nokogiri::Nodeset. But sometimes we'd like to
67
+ operate on just one node (or the first one we
68
+ find). For that there's the @at@ matcher.
69
+
70
+ dress = Dress {
71
+ at("my") do
72
+ me.name = "homer"
73
+ end
74
+ }
75
+
76
+ @me@ is always the object yielded by the matcher
77
+ string. For @match@ it would be a Nodeset, and for
78
+ @at@, it would be an Element.
79
+
80
+ We can define helper methods on a Dress. This is
81
+ one that implements a counter,
82
+
83
+ dress = Dress {
84
+ def count
85
+ @count ||= 0
86
+ @count += 1
87
+ @count
88
+ end
89
+
90
+ match("brain") do
91
+ each { |e| e["area"] = count.to_s }
92
+ end
93
+ }
94
+ puts dress.on(Nokogiri.make { my { brain; brain; brain; brain }}).to_s
95
+ # <my><brain area="1"></brain><brain area="2"></brain><brain area="3"></brain><brain area="4"></brain></my>
96
+
97
+
98
+ We can combine dresses into lines,
99
+
100
+ d1 = Dress { ... }
101
+ d2 = Dress { ... }
102
+ (d1 | d2).on(doc)
103
+
104
+
105
+ We can link lines together,
106
+
107
+ d3 = Dress { ... }
108
+ d4 = Dress { ... }
109
+ ((d1 | d2) | (d3 | d4)).on(doc)
110
+
111
+
112
+ A dress is a class inheriting from Dress.
113
+
114
+ class FancyDress < Dress
115
+ def helper1
116
+ end
117
+ def helper2
118
+ end
119
+ match(...) { ... }
120
+ ...
121
+ end
122
+
123
+ class PrettyDress < Dress
124
+ ...
125
+ end
126
+
127
+
128
+ Then we can combine these dresses,
129
+
130
+ (FancyDress | PrettyDress).on(document)
131
+
132
+ You can of course mix that with dynamic dresses,
133
+
134
+ (FancyDress | PrettyDress | Dress do ... end).on(doc)
135
+
136
+
137
+ h1. That Wasn't So Hard, Was It?
138
+
139
+ So Ruby saved us all from XSLT. Minus all the
140
+ XSLT-inspired buckets of tears, the world is a
141
+ better place. FTW.
142
+
143
+ h1. Copyright
144
+
145
+ Copyright (c) 2009 Howard Yeh. See LICENSE for details.
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "dress"
8
+ gem.summary = %Q{DOM transformation based on nokogiri}
9
+ gem.description = %Q{Inspired by the horror of XSLT}
10
+ gem.email = "hayeah@gmail.com"
11
+ gem.homepage = "http://github.com/hayeah/dress"
12
+ gem.authors = ["Howard Yeh"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "dress #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,92 @@
1
+ require 'nokogiri'
2
+
3
+ class Dress
4
+ class Line
5
+ attr_reader :dresses
6
+ def initialize(dresses)
7
+ @dresses = dresses
8
+ end
9
+
10
+ def |(obj)
11
+ case obj
12
+ when Line
13
+ Line.new(self.dresses + obj.dresses)
14
+ when Dress
15
+ Line.new(self.dresses + [obj])
16
+ else
17
+ raise "expects a Dress or a Line"
18
+ end
19
+ end
20
+
21
+ def length
22
+ dresses.length
23
+ end
24
+
25
+ def on(node)
26
+ @dresses.each do |dress|
27
+ dress.on(node)
28
+ end
29
+ node
30
+ end
31
+ end
32
+
33
+ class << self
34
+ attr_reader :transforms
35
+ def match(matcher,&block)
36
+ @transforms ||= []
37
+ @transforms << [:search,matcher,block]
38
+ end
39
+
40
+ def at(matcher,&block)
41
+ @transforms ||= []
42
+ @transforms << [:at,matcher,block]
43
+ end
44
+
45
+ # destructive transform of node
46
+ def on(node)
47
+ node =
48
+ case node
49
+ when Nokogiri::XML::Node
50
+ node
51
+ when String,IO
52
+ Nokogiri::XML(node)
53
+ else
54
+ raise "bad xml document: #{node}"
55
+ end
56
+ dresser = self.new(node)
57
+ node
58
+ end
59
+
60
+ def style(&block)
61
+ c = Class.new(Dress)
62
+ c.class_eval(&block)
63
+ c
64
+ end
65
+
66
+ def |(dress2)
67
+ raise "expects a Dress" unless dress2.ancestors.include?(Dress)
68
+ Line.new([self,dress2])
69
+ end
70
+ end
71
+
72
+ attr_reader :me
73
+ def initialize(node)
74
+ self.class.transforms.each do |(method,matcher,block)|
75
+ # method == :at | :search
76
+ @me = node.send(method,matcher)
77
+ self.instance_eval(&block) # this is so we can define helper methods on the dress
78
+ end
79
+ end
80
+
81
+ def method_missing(method,*args,&block)
82
+ if block
83
+ @me.send(method,*args,&block)
84
+ else
85
+ @me.send(method,*args)
86
+ end
87
+ end
88
+ end
89
+
90
+ def Dress(&block)
91
+ Dress.style(&block)
92
+ end
@@ -0,0 +1,111 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'pp'
4
+ require 'tempfile'
5
+
6
+ describe "Dress" do
7
+
8
+ def test_data
9
+ Nokogiri.make { foo { bar { baz }}}.parent
10
+ end
11
+
12
+ it "transforms" do
13
+ d = Dress {
14
+ match("bar") do
15
+ wrap(nil) { qux }
16
+ end
17
+
18
+ match("bar") do
19
+ set("attr","oh yay!")
20
+ end
21
+ }
22
+
23
+ node = d.on(test_data)
24
+ (node / "qux bar").should_not be_empty
25
+ end
26
+
27
+ it "transforms xml string or file" do
28
+ d = Dress {
29
+ match("bar") do
30
+ wrap(nil) { qux }
31
+ end
32
+ }
33
+ doc_str = "<bar></bar>"
34
+ r = d.on(doc_str)
35
+ (r / "qux bar").should_not be_empty
36
+
37
+ Tempfile.open("test-xml") do |f|
38
+ f.puts doc_str
39
+ f.flush
40
+ r = d.on(File.new(f.path))
41
+ (r / "qux bar").should_not be_empty
42
+ end
43
+ end
44
+
45
+ it "transforms the first element found with at" do
46
+ d = Dress {
47
+ at("bar") do
48
+ me.name = "bar2"
49
+ end
50
+ }
51
+ r = d.on(test_data)
52
+ (r / "bar2").should_not be_empty
53
+ end
54
+
55
+ it "chains transforms" do
56
+ d1 = Dress {
57
+ match("bar") do
58
+ wrap(nil) { qux }
59
+ end
60
+ }
61
+
62
+ d2 = Dress {
63
+ match("bar") do
64
+ set("attr","oh yay!")
65
+ end
66
+ }
67
+
68
+ line = (d1 | d2)
69
+ line.should be_a(Dress::Line)
70
+ node = line.on(test_data)
71
+ (node / "qux").should_not be_empty
72
+ (node / "qux bar").should_not be_empty
73
+ node.xpath("//@attr").map(&:value).should == ["oh yay!"]
74
+ end
75
+
76
+ it "chains chains" do
77
+ d1 = Dress {
78
+ match("foo") do
79
+ set("a","1")
80
+ end
81
+ }
82
+
83
+ d2 = Dress {
84
+ match("foo") do
85
+ set("b","2")
86
+ end
87
+ }
88
+
89
+ d3 = Dress {
90
+ match("bar") do
91
+ set("a","1")
92
+ end
93
+ }
94
+
95
+ d4 = Dress {
96
+ match("bar") do
97
+ set("b","2")
98
+ end
99
+ }
100
+
101
+ line1 = (d1 | d2)
102
+ line2 = (d3 | d4)
103
+ line = (line1 | line2)
104
+ line.length.should == 4
105
+ node = line.on(test_data)
106
+ node.xpath("foo/@a").map(&:value).should == ["1"]
107
+ node.xpath("foo/@b").map(&:value).should == ["2"]
108
+ node.xpath("//bar/@a").map(&:value).should == ["1"]
109
+ node.xpath("//bar/@b").map(&:value).should == ["2"]
110
+ end
111
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'dress'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dress
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Howard Yeh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-28 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: Inspired by the horror of XSLT
26
+ email: hayeah@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README
39
+ - Rakefile
40
+ - VERSION
41
+ - lib/dress.rb
42
+ - spec/dress_spec.rb
43
+ - spec/spec.opts
44
+ - spec/spec_helper.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/hayeah/dress
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: DOM transformation based on nokogiri
73
+ test_files:
74
+ - spec/dress_spec.rb
75
+ - spec/spec_helper.rb