jarrett-quarto 0.3.1 → 1.1.0

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/README CHANGED
@@ -5,7 +5,7 @@ Quarto was built with HTML output in mind, but there's nothing to prevent you fr
5
5
  to any other format. You could even output to an interpolated scripting language like PHP.
6
6
 
7
7
  Why Quarto?
8
- ============
8
+ ===========
9
9
 
10
10
  Say you have a book in XML format, and you want to make a web site from it. You could transform it
11
11
  to HTML using XSLT. But what if you need logic that can't be implemented in XSLT?
@@ -13,7 +13,13 @@ to HTML using XSLT. But what if you need logic that can't be implemented in XSLT
13
13
  Enter Quarto. Instead of writing a series of XSLT sheets, you write ERB templates. You implement
14
14
  whatever custom logic you need in classes that wrap the DOM elements, then you pass variables to the templates.
15
15
 
16
+ Installation
17
+ ============
18
+
19
+ gem sources -a http://gems.github.com
20
+ sudo gem install jarrett-quarto
21
+
16
22
  Using Quarto
17
- =============
23
+ ============
18
24
 
19
- Thorough documentation doesn't exist yet. For now, see spec/sample_project.
25
+ Thorough documentation doesn't exist yet. For now, see spec/sample_project and the RDoc.
data/Rakefile CHANGED
@@ -10,6 +10,7 @@ begin
10
10
  gemspec.authors = ['Jarrett Colby']
11
11
  gemspec.files = FileList['[A-Z]*', '{bin,lib,test}/**/*']
12
12
  gemspec.executables = 'quarto'
13
+ gemspec.add_dependency('activesupport', '>= 2.3.2')
13
14
  end
14
15
  rescue LoadError
15
16
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 1.1.0
@@ -1,152 +1,240 @@
1
1
  module Quarto
2
- # ElementWrapper subclasses can define parent and child elements, resulting
3
- # in handy accessor methods. For example:
4
- #
5
- # class Company < ElementWrapper
6
- # children :employees
7
- # end
8
- #
9
- # class Employee < ElementWrapper
10
- # parent :company
11
- # element_attr 'name'
12
- # end
13
- #
14
- # # in generate.rb:
15
- # company = Company.find :first
16
- # company.employees.each do |employee|
17
- # puts employee.name
18
- # end
19
-
20
- module ElementWrapperChildren
21
- def self.included(base) # :nodoc:
22
- base.extend(ClassMethods)
23
- base.class_eval do
24
- alias_method :method_missing_without_children, :method_missing
25
- alias_method :method_missing, :method_missing_with_children
26
-
27
- alias_method :respond_to_without_children?, :respond_to?
28
- alias_method :respond_to?, :respond_to_with_children?
2
+ module ElementWrapper # :nodoc:
3
+ module Children # :nodoc:
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ base.class_eval do
7
+ alias_method :method_missing_without_children, :method_missing
8
+ alias_method :method_missing, :method_missing_with_children
9
+
10
+ alias_method :respond_to_without_children?, :respond_to?
11
+ alias_method :respond_to?, :respond_to_with_children?
12
+ end
29
13
  end
30
- end
31
-
32
- def method_missing_with_children(meth, *args) # :nodoc:
33
- if self.class.has_children_named?(meth)
34
- children_proxy(meth)
35
- elsif self.class.has_parent_named?(meth)
36
- wrapped_parent
37
- else
38
- method_missing_without_children(meth, *args)
14
+
15
+ def method_missing_with_children(meth, *args)
16
+ if self.class.has_child_named?(meth)
17
+ child_obj(meth)
18
+ elsif self.class.has_children_named?(meth)
19
+ children_proxy(meth)
20
+ elsif self.class.has_parent_named?(meth)
21
+ wrapped_parent
22
+ else
23
+ method_missing_without_children(meth, *args)
24
+ end
39
25
  end
40
- end
41
-
42
- def respond_to_with_children?(meth, include_private = false) # :nodoc:
43
- if self.class.has_children_named?(meth) or self.class.has_parent_named?(meth)
44
- true
45
- else
46
- respond_to_without_children?(meth, include_private)
26
+
27
+ def respond_to_with_children?(meth, include_private = false)
28
+ if self.class.has_child_named?(meth) or self.class.has_children_named?(meth) or self.class.has_parent_named?(meth)
29
+ true
30
+ else
31
+ respond_to_without_children?(meth, include_private)
32
+ end
47
33
  end
48
- end
49
34
 
50
- protected
51
-
52
- def children_proxy(collection_el_name) # :nodoc:
53
- @children_proxies ||= {}
54
- @children_proxies[collection_el_name.to_s] ||= Children.new(self, collection_el_name.to_s.singularize)
55
- end
35
+ protected
56
36
 
57
- def wrapped_parent # :nodoc:
58
- parent_el_name = self.class.read_inheritable_attribute(:parent)
59
- parent_class_name = parent_el_name.classify
60
- Kernel.const_get(parent_class_name).new(@element.parent.parent) # Go up two levels, since each child is expected to be inside a collection element
61
- end
62
-
63
- module ClassMethods # :nodoc:
64
- # :singleton-method:
65
- # Define children. +el_name+ must be the singular form. In the XML, all children must be
66
- # wrapped in a collection element whose name is the plural form.
67
- #
68
- # Example:
69
- #
70
- # <company>
71
- # <employees>
72
- # <employee>
73
- # </employee>
74
- # </employees>
75
- # </company>
76
- #
77
- # class Company < ElementWrapper
78
- # children :employees
79
- # end
80
- def children(el_name)
81
- write_inheritable_array(:children, [el_name.to_s])
37
+ def child_obj(meth)
38
+ options = self.class.read_inheritable_attribute(:singleton_children)[meth.to_sym]
39
+ el_name = (options[:element_name] || meth).to_s
40
+ child_class = Kernel.const_get(options[:wrapper_class] || el_name.classify)
41
+ child_element = @element.elements[el_name]
42
+ return nil if child_element.nil?
43
+ @singleton_children ||= {}
44
+ @singleton_children[meth] ||= child_class.new(child_element)
82
45
  end
83
46
 
84
- def has_children_named?(collection_el_name) # :nodoc:
85
- return false if read_inheritable_attribute(:children).nil?
86
- read_inheritable_attribute(:children).include?(collection_el_name.to_s)
47
+ def children_proxy(meth)
48
+ options = self.class.read_inheritable_attribute(:children)[meth.to_sym]
49
+ @children_proxies ||= {}
50
+ @children_proxies[meth] ||= ChildrenProxy.new(self, options[:element_name], options)
87
51
  end
88
52
 
89
- def has_parent_named?(parent_el_name) # :nodoc:
90
- read_inheritable_attribute(:parent) == parent_el_name.to_s
53
+ def wrapped_parent
54
+ options = self.class.read_inheritable_attribute(:parent)
55
+ parent_class = Kernel.const_get(options[:wrapper_class] || options[:element_name].classify)
56
+ parent_el = @element
57
+ while parent_el.name != options[:element_name]
58
+ parent_el = parent_el.parent
59
+ end
60
+ parent_class.new(parent_el) # Go up two levels, since each child is expected to be inside a collection element
91
61
  end
92
62
 
93
- # :singleton-method:
94
- # Defines the element's parent. Example:
95
- # Example:
63
+ # ElementWrapper::Base subclasses can define parent and child elements, resulting
64
+ # in handy accessor methods. For example:
96
65
  #
97
- # <company>
98
- # <employees>
99
- # <employee>
100
- # </employee>
101
- # </employees>
102
- # </company>
66
+ # class Company < ElementWrapper::Base
67
+ # children :employees
68
+ # end
103
69
  #
104
- # class Employee < ElementWrapper
70
+ # class Employee < ElementWrapper::Base
105
71
  # parent :company
72
+ # element_attr 'name'
73
+ # end
74
+ #
75
+ # and in generate.rb:
76
+ # company = Company.find :first
77
+ # company.employees.each do |employee|
78
+ # puts employee.name
106
79
  # end
107
- def parent(el_name)
108
- write_inheritable_attribute(:parent, el_name.to_s)
80
+
81
+ module ClassMethods
82
+ # Creates an attribute for a child element. +el_name+ must be the singular form.
83
+ #
84
+ # Options:
85
+ # * <tt>:element_name</tt> - The name of the child element. Defaults to +method_name+.
86
+ # * <tt>:wrapper_class</tt> - <tt>:wrapper_class</tt> - The subclass of ElementWrapper::Base to use.
87
+ # Defaults to the element name.
88
+ #
89
+ # Example:
90
+ #
91
+ # <company>
92
+ # <boss>
93
+ # <name>Joe Schmoe</name>
94
+ # </boss>
95
+ # </company>
96
+ #
97
+ # class Company
98
+ # child :boss
99
+ # end
100
+ def child(method_name, options = {})
101
+ write_inheritable_hash(:singleton_children, {method_name.to_sym => options})
102
+ end
103
+
104
+ # Creates an attribute for child elements.
105
+ #
106
+ # Options:
107
+ # * <tt>:element_name</tt> - The XML element of each individual child. Default
108
+ # to the singular form of +method_name+.
109
+ # * <tt>:collection_element</tt> - By default, ElementWrapper assums that all
110
+ # children are wrapped in a collection element whose name is +method_name+.
111
+ # You can override this with <tt>:collection_element</tt>. If
112
+ # the child elements are not wrapped in a collection element at all,
113
+ # use <tt>:collection_element => nil</tt>.
114
+ # * <tt>:wrapper_class</tt> - The subclass of ElementWrapper::Base to use.
115
+ # Defaults to the singular form of the element name.
116
+ #
117
+ # Example:
118
+ #
119
+ # <company>
120
+ # <employees>
121
+ # <employee>
122
+ # </employee>
123
+ # </employees>
124
+ # </company>
125
+ #
126
+ # class Company < ElementWrapper::Base
127
+ # children :employees
128
+ # end
129
+ def children(method_name, options = {})
130
+ write_inheritable_hash(:children, {method_name.to_sym => {
131
+ :element_name => method_name.to_s.singularize,
132
+ :collection_element => method_name.to_s
133
+ }.merge(options)})
134
+ end
135
+
136
+ def has_child_named?(method_name) # :nodoc:
137
+ return false if read_inheritable_attribute(:singleton_children).nil?
138
+ read_inheritable_attribute(:singleton_children).has_key?(method_name.to_sym)
139
+ end
140
+
141
+ def has_children_named?(method_name) # :nodoc:
142
+ return false if read_inheritable_attribute(:children).nil?
143
+ read_inheritable_attribute(:children).has_key?(method_name.to_sym)
144
+ end
145
+
146
+ def has_parent_named?(method_name) # :nodoc:
147
+ return false if read_inheritable_attribute(:parent).nil?
148
+ read_inheritable_attribute(:parent)[:method] == method_name.to_sym
149
+ end
150
+
151
+ # Defines the element's parent. Options:
152
+ #
153
+ # * <tt>:element_name</tt> - The name of the parent element. Defaults to +method_name+.
154
+ # * <tt>:wrapper_class</tt> - The subclass of ElementWrapper::Base to use.
155
+ # Defaults to the element name.
156
+ #
157
+ # Example:
158
+ #
159
+ # <company>
160
+ # <employees>
161
+ # <employee>
162
+ # </employee>
163
+ # </employees>
164
+ # </company>
165
+ #
166
+ # class Employee < ElementWrapper::Base
167
+ # parent :company
168
+ # end
169
+ def parent(method_name, options = {})
170
+ write_inheritable_attribute(:parent, {:method => method_name.to_sym, :element_name => method_name.to_s}.merge(options))
171
+ end
109
172
  end
110
173
  end
111
- end
112
-
113
- class Children
114
- include Enumerable
115
-
116
- # Returns the REXML::Element for the children collection.
117
- attr_reader :collection_element
118
-
119
- # Iterate over all children.
120
- def each
121
- to_a.each { |child| yield child }
122
- end
123
-
124
- # Returns true if there are no children.
125
- def empty?
126
- to_a.empty?
127
- end
128
174
 
129
- def initialize(wrapped_parent, el_name, options = {}) # :nodoc:
130
- @wrapped_parent = wrapped_parent
131
- @el_name = el_name.to_s
132
- @collection_element = @wrapped_parent.element.elements[options[:collection_el_name] || @el_name.pluralize]
133
- @wrapper_class = options[:wrapper_class] || Kernel.const_get(@el_name.classify)
134
- end
135
-
136
- # Returns the number of children.
137
- def length
138
- to_a.length
139
- end
140
-
141
- # Returns an array of all children. Each is an instance of ElementWrapper. If +xpath+ is provided, the results will be filtered. +xpath+ is relative to the parent element
142
- def to_a(xpath = nil)
143
- @all ||= @collection_element.elements.to_a(xpath || @el_name).collect do |el|
144
- @wrapper_class.new(el)
175
+ # Any call to a children accessor method returns an instance of ChildrenProxy. For example,
176
+ # consider this class:
177
+ #
178
+ # class Company < ElementWrapper::Base
179
+ # children :employees
180
+ # end
181
+ #
182
+ # If you call <tt>#employees</tt> on an instance of Company, you'll get a ChildrenProxy
183
+ # object.
184
+
185
+ class ChildrenProxy
186
+ include Enumerable
187
+
188
+ # Returns the REXML::Element for the children collection.
189
+ attr_reader :collection_element
190
+
191
+ # Iterates over all children.
192
+ def each
193
+ to_a.each { |child| yield child }
145
194
  end
195
+
196
+ # Returns true if there are no children.
197
+ def empty?
198
+ to_a.empty?
199
+ end
200
+
201
+ # Returns the first child in the collection.
202
+ def first
203
+ to_a.first
204
+ end
205
+
206
+ def initialize(wrapped_parent, el_name, options = {}) # :nodoc:
207
+ @wrapped_parent = wrapped_parent
208
+ @el_name = el_name.to_s
209
+ if options[:collection_element].nil?
210
+ # The subclass says there is no collection element wrapping the children
211
+ @collection_element = nil
212
+ else
213
+ @collection_element = @wrapped_parent.element.elements[options[:collection_element]]
214
+ end
215
+ @wrapper_class = Kernel.const_get(options[:wrapper_class] || @el_name.classify)
216
+ end
217
+
218
+ # Returns the last child in the collection.
219
+ def last
220
+ to_a.last
221
+ end
222
+
223
+ # Returns the number of children.
224
+ def length
225
+ to_a.length
226
+ end
227
+
228
+ # Returns an array of all children. Each is an instance of ElementWrapper::Base. If +xpath+ is provided, the results will be filtered. +xpath+ is relative to the parent element
229
+ def to_a(xpath = nil)
230
+ @all ||= (@collection_element || @wrapped_parent.element).elements.to_a(xpath || @el_name).collect do |el|
231
+ @wrapper_class.new(el)
232
+ end
233
+ end
234
+
235
+ alias_method :size, :length
146
236
  end
147
-
148
- alias_method :size, :length
149
237
  end
150
238
  end
151
239
 
152
- Quarto::ElementWrapper.send(:include, Quarto::ElementWrapperChildren)
240
+ Quarto::ElementWrapper::Base.send(:include, Quarto::ElementWrapper::Children)
@@ -1,155 +1,183 @@
1
1
  module Quarto
2
- # Abstract base class for your models. Put your ElementWrapper subclasses inside the "models"
3
- # directory within your project. All files in that directory will be automatically required.
4
- #
5
- # Each ElementWrapper subclass corresponds to exactly one XML element.
6
- # You can specify the model's element name by calling element_name=, but
7
- # generally, you just let ElementWrapper use the default, which is the subclass
8
- # name in snake_case.
9
- #
10
- # Instance attributes corresponding to the XML attributes
11
- #
12
- # For example, suppose you have an XML document like this:
13
- #
14
- # <programmers>
15
- # <programmer skill="genius">
16
- # <name>Linus Torvalds</name>
17
- # <programmer>
18
- # </programmers>
19
- #
20
- # You could then subclass ElementWrapper like this:
21
- #
22
- # class Programmer < ElementWrapper
23
- # element_name = 'programmer'
24
- # element_attrs 'name'
25
- # end
26
- #
27
- # You could then do something like this in your generate.rb file:
28
- #
29
- # programmer = Programmer.find :first
30
- # puts programmer.name
31
- # puts programmer.skill
32
- #
33
- # Also see the documentation for Quarto::Children
2
+ module ElementWrapper # :nodoc:
34
3
 
35
- class ElementWrapper
36
- include InheritableAttributes
37
-
38
- # Returns true if both instances come from the same node in the source XML document.
39
- def ==(other_wrapped_element)
40
- other_wrapped_element.is_a?(Quarto::ElementWrapper) and @element == other_wrapped_element.element
41
- end
42
-
43
- # Returns the currently-loaded REXML::Document.
44
- def self.xml_doc
45
- Quarto.xml_doc
46
- end
47
-
48
- # Returns the REXML::Element from which the instance was created.
49
- attr_reader :element
50
-
51
- # Creates read-only attributes from the given strings. When a model is instantiated from an XML node,
52
- # ElementWrapper will try to populate these attributes using the node's child elements.
4
+ # Abstract base class for your models. Put your ElementWrapper::Base subclasses inside the
5
+ # "models" directory within your project. All files in that directory will be automatically
6
+ # required.
53
7
  #
54
- # For example, if your "employee" element has a child element called "name," you can use:
8
+ # Each ElementWrapper::Base subclass corresponds to exactly one XML element.
9
+ # You can specify the model's element name by calling element_name=, but
10
+ # generally, you just let ElementWrapper use the default, which is the subclass
11
+ # name in snake_case.
55
12
  #
56
- # element_attrs 'name'
13
+ # Inside each ElementWrapper::Base is a REXML::Element. ElementWrapper::Base implements
14
+ # <tt>method_missing</tt>, allowing you to call that REXML::Element's methods
15
+ # on the ElementWrapper::Base instance.
57
16
  #
58
- # ...which will then expose a #name method for every instance of your class. Also see the usage example in the class description.
17
+ # Instance attributes corresponding to the XML attributes will automatically
18
+ # be defined. Hwoever, if you want an attribute defined by the text of a child
19
+ # element, you have to specify it yourself.
59
20
  #
60
- # Remember, XML attributes will automatically have corresponding ElementWrapper attributes. You only need to tell
61
- # ElementWrapper which child elements to use.
62
- def self.element_attrs(*element_names)
63
- write_inheritable_array :element_attrs, element_names.collect { |en| en.to_sym}
64
- end
65
-
66
- # Returns the XML element name.
67
- def self.element_name
68
- @element_name
69
- end
70
-
71
- # Overrides the XML element name. The default is the class name in snake_case.
72
- def self.element_name=(el_name)
73
- @element_name = el_name
74
- end
75
-
76
- # Searches the XML document and returns instances of the class. The first parameter must be either :first, :last, or :all.
77
- # If it's :first or :last, the method returns a single instance or nil. If it's :all, the method returns an array (which may be empty).
21
+ # For example, suppose you have an XML document like this:
22
+ #
23
+ # <programmers>
24
+ # <programmer skill="genius">
25
+ # <name>Linus Torvalds</name>
26
+ # <programmer>
27
+ # </programmers>
28
+ #
29
+ # You could then subclass ElementWrapper like this:
30
+ #
31
+ # class Programmer < ElementWrapper::Base
32
+ # element_name = 'programmer'
33
+ # element_attrs 'name'
34
+ # end
35
+ #
36
+ # You could then do something like this in your generate.rb file:
78
37
  #
79
- # Options:
38
+ # programmer = Programmer.find :first
39
+ # puts programmer.name
40
+ # puts programmer.skill
80
41
  #
81
- # * <tt>:xpath</tt> - An XPath expression to limit the search. If this option is not given, the default XPath is "//element_name"
82
- def self.find(quantifier, options = {})
83
- raise ArgumentError, "Quantifier must be :all, :first, or :last, but got #{quantifier.inspect}" unless [:all, :first, :last].include?(quantifier)
84
- raise ArgumentError, "Options must be a Hash, but got #{options.inspect}" unless options.is_a?(Hash)
85
- if options.has_key?(:xpath)
86
- xpath = options[:xpath]
87
- else
88
- xpath = "//#{@element_name}"
89
- # TODO: add support for :root and :conditions (XPath predicates)
42
+ # Also see the documentation for ElementWrapper::Children
43
+
44
+ class Base
45
+ include InheritableAttributes
46
+
47
+ # Returns true if both instances come from the same node in the source XML document.
48
+ def ==(other_wrapped_element)
49
+ other_wrapped_element.is_a?(Quarto::ElementWrapper::Base) and @element == other_wrapped_element.element
90
50
  end
91
- all = xml_doc.elements.to_a(xpath).collect do |el|
92
- new(el)
51
+
52
+ # Returns the currently-loaded REXML::Document.
53
+ def self.xml_doc
54
+ Quarto.xml_doc
93
55
  end
94
- case quantifier
95
- when :all
96
- all
97
- when :first
98
- all.first
99
- when :last
100
- all.last
56
+
57
+ # Returns the REXML::Element from which the instance was created.
58
+ attr_reader :element
59
+
60
+ # Creates read-only attributes from the given strings. When a model is instantiated from an XML node,
61
+ # ElementWrapper will try to populate these attributes using the node's child elements.
62
+ #
63
+ # For example, if your "employee" element has a child element called "name," you can use:
64
+ #
65
+ # element_attrs 'name'
66
+ #
67
+ # ...which will then expose a #name method for every instance of your class. Also see the usage example in the class description.
68
+ #
69
+ # Remember, XML attributes will automatically have corresponding ElementWrapper attributes. You only need to tell
70
+ # ElementWrapper which child elements to use.
71
+ def self.element_attrs(*element_names)
72
+ write_inheritable_array :element_attrs, element_names.collect { |en| en.to_sym}
101
73
  end
102
- end
103
-
104
- def self.inherited(subclass) # :nodoc:
105
- subclass.element_name = subclass.to_s.underscore
106
- end
107
-
108
- def initialize(el) # :nodoc:
109
- unless el.is_a?(REXML::Element)
110
- raise ArgumentError, "Quarto::ElementWrapper.new must be passed a REXML::Element, but got #{el.inspect}"
74
+
75
+ # Returns the XML element name.
76
+ def self.element_name
77
+ @element_name
111
78
  end
112
- @element = el
113
- @attributes = {}
114
- @element.attributes.each do |a_name, value|
115
- @attributes[a_name.to_sym] = typecast_text(value)
79
+
80
+ # Overrides the XML element name. The default is the class name in snake_case.
81
+ def self.element_name=(el_name)
82
+ @element_name = el_name
116
83
  end
117
- self.class.read_inheritable_attribute(:element_attrs).each do |el_name|
118
- raise ArgumentError, "Expected <#{@element.name}> to contain <#{el_name}>" if @element.elements[el_name.to_s].nil?
119
- @attributes[el_name.to_sym] = typecast_text(@element.elements[el_name.to_s].text)
84
+
85
+ # Searches the XML document and returns instances of the class. The first parameter must be either :first, :last, or :all.
86
+ # If it's :first or :last, the method returns a single instance or nil. If it's :all, the method returns an array (which may be empty).
87
+ #
88
+ # Options:
89
+ #
90
+ # * <tt>:xpath</tt> - An XPath expression to limit the search. If this option is not given, the default XPath is "//element_name"
91
+ def self.find(quantifier, options = {})
92
+ raise ArgumentError, "Quantifier must be :all, :first, or :last, but got #{quantifier.inspect}" unless [:all, :first, :last].include?(quantifier)
93
+ raise ArgumentError, "Options must be a Hash, but got #{options.inspect}" unless options.is_a?(Hash)
94
+ if options.has_key?(:xpath)
95
+ xpath = options[:xpath]
96
+ else
97
+ xpath = "//#{@element_name}"
98
+ # TODO: add support for :root and :conditions (XPath predicates)
99
+ end
100
+ all = xml_doc.elements.to_a(xpath)
101
+ case quantifier
102
+ when :all
103
+ all.collect { |el| new(el) }
104
+ when :first
105
+ all.empty? ? nil : new(all.first)
106
+ when :last
107
+ all.empty? ? nil : new(all.last)
108
+ end
120
109
  end
121
- end
122
-
123
- def method_missing(meth, *args, &block) # :nodoc:
124
- if @attributes.has_key?(meth.to_sym)
125
- @attributes[meth.to_sym]
126
- elsif @element.respond_to?(meth)
127
- @element.send(meth, *args, &block)
128
- else
129
- super
110
+
111
+ def self.inherited(subclass) # :nodoc:
112
+ subclass.element_name = subclass.to_s.underscore
130
113
  end
131
- end
132
-
133
- def respond_to?(meth, include_private = false) # :nodoc:
134
- if @element.respond_to?(meth, include_private) or @attributes.has_key?(meth.to_sym)
135
- true
136
- else
137
- super
114
+
115
+ def initialize(el) # :nodoc:
116
+ unless el.is_a?(REXML::Element)
117
+ raise ArgumentError, "Quarto::ElementWrapper.new must be passed a REXML::Element, but got #{el.inspect}"
118
+ end
119
+ @element = el
120
+ @attributes = {}
121
+ @element.attributes.each do |a_name, value|
122
+ @attributes[a_name.to_sym] = typecast_text(value)
123
+ end
124
+ if element_attrs = self.class.read_inheritable_attribute(:element_attrs)
125
+ element_attrs.each do |el_name|
126
+ raise ArgumentError, "Expected <#{@element.name}> to have child <#{el_name}>" if @element.elements[el_name.to_s].nil?
127
+ @attributes[el_name.to_sym] = typecast_text(@element.elements[el_name.to_s].text)
128
+ end
129
+ end
130
+ if text_attr = self.class.read_inheritable_attribute(:text_attr)
131
+ @attributes[text_attr.to_sym] = typecast_text(@element.text)
132
+ end
138
133
  end
139
- end
140
-
141
- protected
142
-
143
- # When an ElementWrapper is instantiated from an XML node, all values start out as strings. This method typecasts those values.
144
- def typecast_text(t)
145
- if t.nil? or (t.is_a?(String) and t.empty?)
146
- nil
147
- elsif t =~ /^-?[0-9]+$/
148
- t.to_i
149
- elsif t =~ /^-?[0-9]*\.[0-9]+$/
150
- t.to_f
151
- else
152
- t
134
+
135
+ def method_missing(meth, *args, &block) # :nodoc:
136
+ if @attributes.has_key?(meth.to_sym)
137
+ @attributes[meth.to_sym]
138
+ elsif @element.respond_to?(meth)
139
+ @element.send(meth, *args, &block)
140
+ else
141
+ super
142
+ end
143
+ end
144
+
145
+ def respond_to?(meth, include_private = false) # :nodoc:
146
+ if @element.respond_to?(meth, include_private) or @attributes.has_key?(meth.to_sym)
147
+ true
148
+ else
149
+ super
150
+ end
151
+ end
152
+
153
+ # Creates a read-only attribute from the wrapped element's text.
154
+ # +attr_name+ can be anything you want; it doesn't have to correspond
155
+ # to anything in the XML. Example:
156
+ #
157
+ # <company>
158
+ # <product>Shoes</product>
159
+ # <company>
160
+ #
161
+ # class Product < ElementWrapper
162
+ # text_attr :name
163
+ # end
164
+ def self.text_attr(attr_name)
165
+ write_inheritable_attribute(:text_attr, attr_name)
166
+ end
167
+
168
+ protected
169
+
170
+ # When an ElementWrapper is instantiated from an XML node, all values start out as strings. This method typecasts those values.
171
+ def typecast_text(t)
172
+ if t.nil? or (t.is_a?(String) and t.empty?)
173
+ nil
174
+ elsif t =~ /^-?[0-9]+$/
175
+ t.to_i
176
+ elsif t =~ /^-?[0-9]*\.[0-9]+$/
177
+ t.to_f
178
+ else
179
+ t
180
+ end
153
181
  end
154
182
  end
155
183
  end
@@ -1,5 +1,3 @@
1
- # Thanks to ActiveSupport for this stuff
2
-
3
1
  module Quarto
4
2
  module InheritableAttributes # :nodoc: all
5
3
  def self.included(base)
@@ -1,6 +1,9 @@
1
1
  require 'cgi'
2
2
 
3
3
  module Quarto
4
+
5
+ # This module is included in Generator and thus made available in <tt>generate.rb</tt> files.
6
+
4
7
  module UrlHelper
5
8
  # Generates an absolute URL, using the <tt>:site_root</tt> config value. (To change <tt>:site_root</tt>,
6
9
  # put something like this in <tt>generate.rb</tt>:
data/lib/quarto.rb CHANGED
@@ -13,4 +13,30 @@ require 'quarto/element_wrapper'
13
13
  require 'quarto/children'
14
14
  require 'quarto/rendering'
15
15
  require 'quarto/generator'
16
- require 'quarto/init_project'
16
+ require 'quarto/init_project'
17
+
18
+ # Quarto is a Ruby framework for generating collections of documents from XML. Potential applications
19
+ # include web sites and e-books. It's built on top of ERB and REXML.
20
+ #
21
+ # Quarto was built with HTML output in mind, but there's nothing to prevent you from outputting
22
+ # to any other format. You could even output to an interpolated scripting language like PHP.
23
+ #
24
+ # =Why Quarto?
25
+ #
26
+ # Say you have a book in XML format, and you want to make a web site from it. You could transform it
27
+ # to HTML using XSLT. But what if you need logic that can't be implemented in XSLT?
28
+ #
29
+ # Enter Quarto. Instead of writing a series of XSLT sheets, you write ERB templates. You implement
30
+ # whatever custom logic you need in classes that wrap the DOM elements, then you pass variables to the templates.
31
+ #
32
+ # =Installation
33
+ #
34
+ # gem sources -a http://gems.github.com
35
+ # sudo gem install jarrett-quarto
36
+ #
37
+ # =Using Quarto
38
+ #
39
+ # Thorough documentation doesn't exist yet. For now, see spec/sample_project and the RDoc.
40
+
41
+ module Quarto
42
+ end
@@ -1,22 +1,18 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/company')
3
- require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/employee')
2
+ require File.expand_path(File.dirname(__FILE__) + '/sample_models')
4
3
 
5
- describe Quarto::ElementWrapperChildren do
4
+ describe Quarto::ElementWrapper::Children do
6
5
  before :each do
7
6
  Quarto.xml_source = File.open(SAMPLE_DIR + '/xml/companies.xml')
8
7
  @xml = Quarto.xml_doc
9
8
  end
10
9
 
11
- context 'an ElementWrapper instance with children' do
12
- before :each do
10
+ context '.children' do
11
+ it 'should create a children accessor' do
13
12
  @element = @xml.elements['companies/company']
14
13
  @company = Company.new(@element)
15
- end
16
-
17
- it 'should know its children' do
18
14
  @company.should respond_to(:employees)
19
- @company.employees.should be_a(Quarto::Children)
15
+ @company.employees.should be_a(Quarto::ElementWrapper::ChildrenProxy)
20
16
  @company.employees.length.should == 2 # In the sample XML file, 37Signals has two employees.
21
17
  @company.employees.each do |employee|
22
18
  employee.should be_a(Employee)
@@ -24,21 +20,177 @@ describe Quarto::ElementWrapperChildren do
24
20
  end
25
21
  end
26
22
 
27
- context 'an ElementWrapper instance with a parent' do
28
- before :each do
23
+ context '.children given :wrapper_class' do
24
+ it 'should use the specified class instead of the default' do
25
+ class CompanyWithWrapperClass < Quarto::ElementWrapper::Base
26
+ element_name = 'company'
27
+ children :employees, :wrapper_class => 'CrazyEmployee'
28
+ end
29
+
30
+ class CrazyEmployee < Quarto::ElementWrapper::Base; end
31
+
32
+ @element = @xml.elements['companies/company']
33
+ @company = CompanyWithWrapperClass.new(@element)
34
+ @company.employees.each do |employee|
35
+ employee.should be_a(CrazyEmployee)
36
+ end
37
+
38
+ Object.class_eval do
39
+ remove_const :CompanyWithWrapperClass
40
+ remove_const :CrazyEmployee
41
+ end
42
+ end
43
+ end
44
+
45
+ context '.children given :collection_element' do
46
+ it 'should use the specified collection element instead of the default' do
47
+ @company = Company.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
48
+ @company.should respond_to(:products)
49
+ @company.products.should be_a(Quarto::ElementWrapper::ChildrenProxy)
50
+ @company.products.length.should == 2
51
+ @company.products.each do |product|
52
+ product.should be_a(Product)
53
+ end
54
+ end
55
+ end
56
+
57
+ context '.children given :collection_element => nil' do
58
+ it 'should create a children accessor that uses immediate children of the element' do
59
+ @company = Company.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
60
+ @company.should respond_to(:locations)
61
+ @company.locations.should be_a(Quarto::ElementWrapper::ChildrenProxy)
62
+ @company.locations.length.should == 2
63
+ @company.locations.each do |location|
64
+ location.should be_a(Location)
65
+ end
66
+ end
67
+ end
68
+
69
+ context '.chldren given :element_name' do
70
+ it 'should use the specified element instead of the default' do
71
+ class CompanyWithElementName < Quarto::ElementWrapper::Base
72
+ children :the_employees, :element_name => 'employee'
73
+ end
74
+
75
+ @element = @xml.elements['companies/company']
76
+ @company = CompanyWithElementName.new(@element)
77
+ @company.the_employees.each do |employee|
78
+ employee.should be_a(Employee)
79
+ employee.element.name.should == 'employee'
80
+ end
81
+
82
+ Object.class_eval do
83
+ remove_const :CompanyWithElementName
84
+ end
85
+ end
86
+ end
87
+
88
+ context '.parent for an element in a collection' do
89
+ it 'should create an accessor for the parent object' do
29
90
  @element = @xml.elements['companies/company/employees/employee']
30
91
  @employee = Employee.new(@element)
31
- end
32
-
33
- it 'should have children which know their parent' do
34
92
  @employee.should respond_to(:company)
35
93
  @employee.company.should be_a(Company)
36
94
  @employee.company.name.should == '37Signals'
37
95
  end
38
96
  end
97
+
98
+ context '.parent for an element that\'s not in a collection' do
99
+ it 'should create an accessor for the parent object' do
100
+ @company = Company.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
101
+ @location= @company.locations.first
102
+ @location.company.should == @company
103
+ end
104
+ end
105
+
106
+ context '.parent given :element_name' do
107
+ it 'should test for something' do
108
+ # create classes
109
+ class EmployeeWithElementName < Quarto::ElementWrapper::Base
110
+ parent :the_company, :element_name => 'company'
111
+ end
112
+
113
+ @element = @xml.elements['//employee']
114
+ @employee = EmployeeWithElementName.new(@element)
115
+ @employee.the_company.should be_a(Company)
116
+ @employee.the_company.element.name.should == 'company'
117
+
118
+ Object.class_eval do
119
+ remove_const :EmployeeWithElementName
120
+ end
121
+ end
122
+ end
123
+
124
+ context '.parent given :wrapper_class' do
125
+ it 'should use the specified class instead of the default' do
126
+ class EmployeeWithWrapperClass < Quarto::ElementWrapper::Base
127
+ parent :company, :wrapper_class => 'CrazyCompany'
128
+ end
129
+
130
+ class CrazyCompany < Quarto::ElementWrapper::Base; end
131
+
132
+ @element = @xml.elements['//employee']
133
+ @employee = EmployeeWithWrapperClass.new(@element)
134
+ @employee.company.should be_a(CrazyCompany)
135
+
136
+ Object.class_eval do
137
+ remove_const :EmployeeWithWrapperClass
138
+ remove_const :CrazyCompany
139
+ end
140
+ end
141
+ end
142
+
143
+ context '.child' do
144
+ before :each do
145
+ @company = Company.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
146
+ end
147
+
148
+ it 'should create an accessor that returns the child if it exists' do
149
+ @company.mascot.should be_a(Mascot)
150
+ end
151
+
152
+ it 'should create an accessor that returns nil if the child does not exist' do
153
+ @company = Company.find :first
154
+ @company.mascot.should == nil
155
+ end
156
+ end
157
+
158
+ context '.child given :wrapper_class' do
159
+ it 'should use the specified class instead of the default' do
160
+ class CompanyWithWrapperClass < Quarto::ElementWrapper::Base
161
+ child :mascot, :wrapper_class => 'CrazyMascot'
162
+ end
163
+
164
+ class CrazyMascot < Quarto::ElementWrapper::Base; end
165
+
166
+ @company = CompanyWithWrapperClass.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
167
+ @company.mascot.should be_a(CrazyMascot)
168
+
169
+ Object.class_eval do
170
+ remove_const :CompanyWithWrapperClass
171
+ remove_const :CrazyMascot
172
+ end
173
+ end
174
+ end
175
+
176
+ context '.child given :element_name' do
177
+ it 'should use the specified element name instead of the default' do
178
+ class CompanyWithElementName < Quarto::ElementWrapper::Base
179
+ child :the_mascot, :element_name => 'mascot'
180
+ end
181
+
182
+ @company = CompanyWithElementName.find(:first, :xpath => "//company[name='Mega-lo-Mart']")
183
+ @company.the_mascot.should be_a(Mascot)
184
+ @company.the_mascot.element.name.should == 'mascot'
185
+
186
+ Object.class_eval do
187
+ remove_const :CompanyWithElementName
188
+ end
189
+ end
190
+ end
39
191
  end
40
192
 
41
- describe Quarto::Children do
193
+ describe Quarto::ElementWrapper::ChildrenProxy do
42
194
  before :each do
43
195
  Quarto.xml_source = File.open(SAMPLE_DIR + '/xml/companies.xml')
44
196
  @xml = Quarto.xml_doc
@@ -52,7 +204,17 @@ describe Quarto::Children do
52
204
  end
53
205
  end
54
206
 
55
- it 'should support empty?' do
207
+ it 'should support #first' do
208
+ @company.employees.should respond_to(:first)
209
+ @company.employees.first.should == @company.employees.to_a.first
210
+ end
211
+
212
+ it 'should support #last' do
213
+ @company.employees.should respond_to(:last)
214
+ @company.employees.last.should == @company.employees.to_a.last
215
+ end
216
+
217
+ it 'should support #empty?' do
56
218
  @company.employees.should respond_to(:empty?)
57
219
  @company.employees.empty?.should == false
58
220
  end
@@ -1,24 +1,23 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/company')
3
- require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/employee')
2
+ require File.expand_path(File.dirname(__FILE__) + '/sample_models')
4
3
 
5
- describe Quarto::ElementWrapper do
4
+ describe Quarto::ElementWrapper::Base do
6
5
  before :each do
7
6
  Quarto.xml_source = File.open(SAMPLE_DIR + '/xml/companies.xml')
8
7
  @xml = Quarto.xml_doc
9
8
  end
10
9
 
11
- context 'wrapping an element with attributes and children' do
10
+ context 'wrapping an element with attributes' do
12
11
  before :each do
13
12
  @element = @xml.elements['companies/company']
14
13
  @company = Company.new(@element)
15
14
  end
16
15
 
17
- it 'should instantiate a subclass of Quarto::ElementWrapper' do
18
- @company.should be_a(Quarto::ElementWrapper)
16
+ it 'should instantiate a subclass of Quarto::ElementWrapper::Base' do
17
+ @company.should be_a(Quarto::ElementWrapper::Base)
19
18
  end
20
19
 
21
- it 'should define methods from specified child elements' do
20
+ it 'should define attributes from specified elements' do
22
21
  @company.should respond_to(:name)
23
22
  @company.name.should == '37Signals'
24
23
  end
@@ -41,6 +40,16 @@ describe Quarto::ElementWrapper do
41
40
  end
42
41
  end
43
42
 
43
+ context 'wrapping an element that only contains text' do
44
+ before :each do
45
+ @product = Product.find(:first, :xpath => "//product[text()='Propane']")
46
+ end
47
+
48
+ it 'should link an attribute to the element\'s text' do
49
+ @product.name.should == 'Propane'
50
+ end
51
+ end
52
+
44
53
  context '.new' do
45
54
  it 'should raise ArgumentError if it is passed anything other than a REXML::Element' do
46
55
  [nil, 'foo', 1].each do |bad|
@@ -53,6 +62,7 @@ describe Quarto::ElementWrapper do
53
62
  it 'should find matching elements based on :xpath' do
54
63
  companies = Company.find(:all, :xpath => 'companies/company')
55
64
  companies.should be_a(Array)
65
+ companies.length.should == 5
56
66
  companies.each do |company|
57
67
  company.should be_a(Company)
58
68
  end
@@ -67,6 +77,15 @@ describe Quarto::ElementWrapper do
67
77
  companies[0].name.should == 'Milliways'
68
78
  end
69
79
 
80
+ it 'should return an empty array with :all and an XPath that yields nothing' do
81
+ Company.find(:all, :xpath => "companies/company[foo='bar']").should == []
82
+ end
83
+
84
+ it 'should return nil with :first or :last and an XPath that yields nothing' do
85
+ Company.find(:first, :xpath => "companies/company[foo='bar']").should == nil
86
+ Company.find(:last, :xpath => "companies/company[foo='bar']").should == nil
87
+ end
88
+
70
89
  it 'should work without the :xpath parameter' do
71
90
  Company.find(:all).should == Company.find(:all, :xpath => '//company')
72
91
  Employee.find(:all).should == Employee.find(:all, :xpath => '//employee')
@@ -89,5 +108,24 @@ describe Quarto::ElementWrapper do
89
108
  company.name.should == 'Milliways'
90
109
  end
91
110
  end
111
+
112
+ context '#==' do
113
+ before :each do
114
+ @element_1 = @xml.elements['companies/company']
115
+ @element_2 = @xml.elements['companies/company[last()]']
116
+ end
117
+
118
+ it 'should return true for two ElementWrapper::Base instances that wrap the same element' do
119
+ Quarto::ElementWrapper::Base.new(@element_1).should == Quarto::ElementWrapper::Base.new(@element_1)
120
+ end
121
+
122
+ it 'should return false for two ElementWrapper::Base instaces that wrap different elements' do
123
+ Quarto::ElementWrapper::Base.new(@element_1).should_not == Quarto::ElementWrapper::Base.new(@element_2)
124
+ end
125
+
126
+ it 'should return false if the second object is not an instance of ElementWrapper::Base' do
127
+ Quarto::ElementWrapper::Base.new(@element_1).should_not == 'foo'
128
+ end
129
+ end
92
130
  end
93
131
 
@@ -32,7 +32,7 @@ describe Quarto do
32
32
  @mock_generator.should_receive(:generate)
33
33
  end
34
34
 
35
- it 'should execute the block in the context of the Generator' do
35
+ it 'should pass the block to the Generator' do
36
36
  @mock_generator.should_receive(:do_something_in_the_block)
37
37
  end
38
38
  end
@@ -114,7 +114,7 @@ describe Quarto::Generator do
114
114
 
115
115
  it 'should pass the companies into the template' do
116
116
  html = File.read(@generator.output_path + '/companies.html')
117
- ['37Signals', 'Mega-lo-mart', 'Kwik-E-Mart', 'Good Burger', 'Milliways'].each do |name|
117
+ ['37Signals', 'Mega-lo-Mart', 'Kwik-E-Mart', 'Good Burger', 'Milliways'].each do |name|
118
118
  html.should include(name)
119
119
  end
120
120
  end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/company')
2
+ require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/employee')
3
+ require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/product')
4
+ require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/location')
5
+ require File.expand_path(File.dirname(__FILE__) + '/sample_project/models/mascot')
@@ -1,8 +1,14 @@
1
- class Company < Quarto::ElementWrapper
1
+ class Company < Quarto::ElementWrapper::Base
2
2
  element_attrs :name, :industry
3
3
 
4
4
  children :employees
5
5
 
6
+ children :products, :collection_element => 'selling'
7
+
8
+ children :locations, :collection_element => nil
9
+
10
+ child :mascot
11
+
6
12
  def competitors
7
13
  @competitors ||= self.class.find(:all, :xpath => "companies/company[industry='#{industry}' and name!='#{name}']")
8
14
  end
@@ -1,4 +1,4 @@
1
- class Employee < Quarto::ElementWrapper
1
+ class Employee < Quarto::ElementWrapper::Base
2
2
  element_attrs :name
3
3
 
4
4
  parent :company
@@ -0,0 +1,5 @@
1
+ class Location < Quarto::ElementWrapper::Base
2
+ parent :company
3
+
4
+ element_attrs 'city', 'state'
5
+ end
@@ -0,0 +1,5 @@
1
+ class Mascot < Quarto::ElementWrapper::Base
2
+ parent :company
3
+
4
+ element_attrs 'name'
5
+ end
@@ -0,0 +1,5 @@
1
+ class Product < Quarto::ElementWrapper::Base
2
+ parent :company
3
+
4
+ text_attr :name
5
+ end
data/spec/spec_helper.rb CHANGED
@@ -9,4 +9,8 @@ require(File.expand_path(File.dirname(__FILE__)) + '/../lib/quarto')
9
9
 
10
10
  Dir.glob(SPEC_DIR + '/matchers/*.rb').each do |matcher_lib|
11
11
  require matcher_lib
12
+ end
13
+
14
+ def puts(str)
15
+ raise "puts('#{str}') called"
12
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jarrett-quarto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarrett Colby
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-05 00:00:00 -07:00
12
+ date: 2009-06-06 00:00:00 -07:00
13
13
  default_executable: quarto
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.2
24
+ version:
16
25
  description: Quarto is a Ruby framework for generating collections of documents from XML. It steps in where XSLT just won't cut it. Potential applications include web sites and e-books. It's built on top of ERB and REXML.
17
26
  email: jarrett@uchicago.edu
18
27
  executables:
@@ -36,8 +45,7 @@ files:
36
45
  - lib/quarto/rendering.rb
37
46
  - lib/quarto/url_helper.rb
38
47
  - lib/quarto/xml_doc.rb
39
- - quarto.gemspec
40
- has_rdoc: true
48
+ has_rdoc: false
41
49
  homepage: http://github.com/jarrett/quarto
42
50
  post_install_message:
43
51
  rdoc_options:
@@ -61,7 +69,7 @@ requirements: []
61
69
  rubyforge_project:
62
70
  rubygems_version: 1.2.0
63
71
  signing_key:
64
- specification_version: 2
72
+ specification_version: 3
65
73
  summary: generates HTML or any other format from XML
66
74
  test_files:
67
75
  - spec/children_spec.rb
@@ -69,8 +77,12 @@ test_files:
69
77
  - spec/generator_spec.rb
70
78
  - spec/init_project_spec.rb
71
79
  - spec/matchers/file_matchers.rb
80
+ - spec/sample_models.rb
72
81
  - spec/sample_project/generate.rb
73
82
  - spec/sample_project/models/company.rb
74
83
  - spec/sample_project/models/employee.rb
84
+ - spec/sample_project/models/location.rb
85
+ - spec/sample_project/models/mascot.rb
86
+ - spec/sample_project/models/product.rb
75
87
  - spec/spec_helper.rb
76
88
  - spec/url_helper_spec.rb
data/quarto.gemspec DELETED
@@ -1,62 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- Gem::Specification.new do |s|
4
- s.name = %q{quarto}
5
- s.version = "0.3.1"
6
-
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Jarrett Colby"]
9
- s.date = %q{2009-06-05}
10
- s.default_executable = %q{quarto}
11
- s.description = %q{Quarto is a Ruby framework for generating collections of documents from XML. It steps in where XSLT just won't cut it. Potential applications include web sites and e-books. It's built on top of ERB and REXML.}
12
- s.email = %q{jarrett@uchicago.edu}
13
- s.executables = ["quarto"]
14
- s.extra_rdoc_files = [
15
- "README"
16
- ]
17
- s.files = [
18
- "README",
19
- "Rakefile",
20
- "VERSION",
21
- "bin/quarto",
22
- "lib/quarto.rb",
23
- "lib/quarto/children.rb",
24
- "lib/quarto/config.rb",
25
- "lib/quarto/element_wrapper.rb",
26
- "lib/quarto/generator.rb",
27
- "lib/quarto/inheritable_attributes.rb",
28
- "lib/quarto/init_project.rb",
29
- "lib/quarto/rendering.rb",
30
- "lib/quarto/url_helper.rb",
31
- "lib/quarto/xml_doc.rb",
32
- "quarto.gemspec"
33
- ]
34
- s.has_rdoc = true
35
- s.homepage = %q{http://github.com/jarrett/quarto}
36
- s.rdoc_options = ["--charset=UTF-8"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.1}
39
- s.summary = %q{generates HTML or any other format from XML}
40
- s.test_files = [
41
- "spec/children_spec.rb",
42
- "spec/element_wrapper_spec.rb",
43
- "spec/generator_spec.rb",
44
- "spec/init_project_spec.rb",
45
- "spec/matchers/file_matchers.rb",
46
- "spec/sample_project/generate.rb",
47
- "spec/sample_project/models/company.rb",
48
- "spec/sample_project/models/employee.rb",
49
- "spec/spec_helper.rb",
50
- "spec/url_helper_spec.rb"
51
- ]
52
-
53
- if s.respond_to? :specification_version then
54
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
- s.specification_version = 2
56
-
57
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
- else
59
- end
60
- else
61
- end
62
- end