jig 0.1.0 → 0.1.2

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.
Binary file
@@ -1,5 +1,18 @@
1
- == 0.1.0 / 2007-05-17
1
+ == 0.1.1 / 2008-02-28
2
+
3
+ * Jig#syntax to help with jig comparison
4
+ * Jig#terse_inspect to help with nested inspect and gaps with processing
5
+ * testing of Jig#parse
6
+ * to_yaml of jigs with procs
7
+ * Callable objects are now rendered to strings via to_yaml
8
+ * Attribute gaps have their own class now instead of being adhoc gap filters.
9
+ * Gap#== checks classes
10
+ * Gap with filter was being replaced by a plain gap.
11
+ Ignore filling if filling is the same gap.
12
+ * Jig#parse correction
13
+ * cleanup up some documentation typos
14
+ * reimplemented Jig.parse with StringScanner, fixed bugs
15
+
16
+ == 0.1.0 / 2007-05-11
17
+
2
18
  * Initial Release
3
- * basic jig support
4
- * XML, XHMTL support
5
- * CSS support (experimental)
@@ -1,3 +1,4 @@
1
+ .DS_Store
1
2
  History.txt
2
3
  Manifest.txt
3
4
  README.txt
@@ -6,7 +7,11 @@ lib/jig.rb
6
7
  lib/jig/css.rb
7
8
  lib/jig/xhtml.rb
8
9
  lib/jig/xml.rb
10
+ test/jig.rb
11
+ test/jig_spec.rb
9
12
  test/test_css.rb
10
13
  test/test_jig.rb
14
+ test/test_parse.rb
11
15
  test/test_xhtml.rb
12
16
  test/test_xml.rb
17
+ todo
data/README.txt CHANGED
@@ -1,9 +1,9 @@
1
1
  Jig
2
2
  by Gary R. Wright
3
- (jig.rubyforge.org)
3
+ http://jig.rubyforge.org
4
4
 
5
5
  == DESCRIPTION:
6
-
6
+
7
7
  A jig is a data structure designed to facilitate construction and
8
8
  manipulation of strings with an inherent hierarchical syntax. The
9
9
  idea is derived from the <bigwig> project (http://www.brics.dk/bigwig/)
@@ -15,7 +15,7 @@ other tools.
15
15
 
16
16
  A jig is an ordered sequence of objects (usually strings) and named
17
17
  _gaps_. When rendered as a string by Jig#to_s, the objects are
18
- rendered by calling #to_s on each object in order. The gaps are skipped.
18
+ rendered calling #to_s on each object in order. The gaps are skipped.
19
19
 
20
20
  A new jig may be constructed from an existing jig by 'plugging' one
21
21
  or more of the named gaps. The new jig shares the objects and their
@@ -24,14 +24,10 @@ the 'plug'. Gaps may be plugged by any object or sequence of
24
24
  objects. When a gap is plugged with another jig, the contents
25
25
  (including gaps) are incorporated into the new jig.
26
26
 
27
- In addition to strings and gaps, a Jig may contain a proc object
28
- (or lambda or method). Procs within a jig are not evaluated until
29
- the jig is rendered as a string by #to_s.
30
-
31
27
  Several subclasses (Jig::XML, Jig::XHTML, Jig::CSS) are defined to
32
28
  help in the construction of XML, XHTML, and CSS documents.
33
29
 
34
- This is a jig with a single gap named <tt>:alpha</tt>
30
+ This is a jig with a single gap named :alpha.
35
31
  Jig.new(:alpha) # => <#Jig: [:alpha]>
36
32
  This is a jig with two objects, 'before' and 'after' separated by
37
33
  a gap named :middle.
@@ -42,44 +38,32 @@ This operation doesn't change j. It can be used again:
42
38
  j.plug(:middle, " and ") # => #<Jig: ["before", " and ", "after"]>
43
39
  There is a destructive version of plug that modifies
44
40
  the jig in place:
45
- j.plug!(:middle, "filled") # => #<Jig: ["before", "filled", "after"]>
46
- j # => #<Jig: ["before", "filled", "after"]>
41
+ j.plug!(:middle, "filled") # => #<Jig: ["before", "filled", "after"]>
42
+ j # => #<Jig: ["before", "filled", "after"]>
47
43
  There are a number of ways to construct a Jig and many of
48
44
  them insert an implicit gap into the Jig. This gap is
49
- identified as <tt>:___</tt> and is used as the default gap
45
+ identified as :___ and is used as the default gap
50
46
  for plug operations when one isn't provided:
51
47
 
52
- puts Jig.new("A", :___, "C").plug("B") # => ABC
53
- puts Jig.new.plug('filled') # => filled
54
-
55
- The % operator is an alias for Jig#plug
48
+ puts Jig.new("A", :___, "C").plug("B") # => ABC
56
49
 
57
- name = Jig.new(:last, ", ", :first)
58
- data = {:first => 'Gary', :last => 'Wright')
59
- puts name % data # => Wright, Gary
50
+ In order to make Jig's more useful for HTML generation,
51
+ the Jig::XHTML class supports a variety of convenience methods;
60
52
 
61
- In order to make Jig's more useful for XML generation,
62
- the Jig::XML class supports a variety of convenience methods;
63
-
64
- X = Jig::XML
65
- puts b = X.element("body") # => <body></body>
66
- puts b.plug("text") # => <body>text</body>
53
+ HT = Jig::XHTML
54
+ puts b = HT.element("body") # => <body></body>
55
+ puts b.plug("text") # => <body>text</body>
67
56
 
68
57
  Method missing makes this even simpler:
69
58
 
70
59
  b = HT.span
71
- puts b.plug("text") # => <span>text</span>
60
+ puts b.plug("text") # => <span>text</span>
72
61
 
73
62
  Attributes can be specified with a hash:
74
63
 
75
- summary = X.p(:class => "summary")
64
+ summary = HT.p(:class => "summary")
76
65
  puts summary.plug("This is a summary") # => <p class="summary">This is a summary</p>
77
66
 
78
- See the Jig::XML for more information about attributes and gaps.
79
- The Jig::XHTML class has a variety of convenience methods for
80
- constructing XHTML documents. The Jig::CSS class generates jigs
81
- that represent CSS rules.
82
-
83
67
  == REQUIREMENTS:
84
68
 
85
69
  * Ruby 1.8
data/Rakefile CHANGED
@@ -7,12 +7,12 @@ require './lib/jig.rb'
7
7
 
8
8
  Hoe.new('jig', Jig::VERSION) do |p|
9
9
  p.rubyforge_name = 'jig'
10
- p.author = ["Gary Wright"]
11
- p.email = 'gwright@rubyforge.org'
12
- p.summary = 'a data structure that supports construction and manipulation of strings with hierarchical structure'
10
+ p.summary = 'A Jig is a data structure that supports construction and manipulation of strings with hierarchical structure.'
13
11
  p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
14
- p.url = "http://jig.rubyforge.org"
15
12
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
13
+ p.author = "Gary R. Wright"
14
+ p.email = "gwright@rubyforge.org"
15
+ p.url = "http://jig.rubyforge.org"
16
16
  end
17
17
 
18
18
  task :spec do
@@ -31,7 +31,7 @@ RCOV = "rcov"
31
31
  desc "generate a unit test coverage report in coverage/unit; see coverage/unit/index.html afterwards"
32
32
  task :rcov do
33
33
  tests = FileList['test/test_*.rb']
34
- sh "#{RCOV} --xref -Ilib #{tests}"
34
+ sh "#{RCOV} -Ilib #{tests}"
35
35
  end
36
36
 
37
37
 
data/lib/jig.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'strscan'
1
2
 
2
3
  =begin rdoc
3
4
  A jig is an ordered sequence of objects and named gaps. During construction,
@@ -39,7 +40,7 @@ replaced with the same sequence of objects.
39
40
  puts j.plug(:separator, '/') # => "first/middle/last"
40
41
  =end
41
42
  class Jig
42
- VERSION = '0.1.0'
43
+ VERSION = '0.1.2'
43
44
  autoload :XML, "jig/xml"
44
45
  autoload :XHTML, "jig/xhtml"
45
46
  autoload :CSS, "jig/css"
@@ -52,7 +53,6 @@ class Jig
52
53
  class Gap
53
54
  ATTRS = :__a
54
55
  GAP = :___
55
-
56
56
  # the name associated with the gap
57
57
  attr :name
58
58
 
@@ -67,7 +67,11 @@ class Jig
67
67
  end
68
68
 
69
69
  def inspect
70
- "#<Gap: [#{name}, #{filter.inspect}]>"
70
+ "#<#{self.class.name}: [#{name}, #{filter.inspect}]>"
71
+ end
72
+
73
+ def terse_inspect
74
+ filter && "#{name}{}".to_sym || name
71
75
  end
72
76
 
73
77
  # Pass the replacement items through the filter.
@@ -78,7 +82,9 @@ class Jig
78
82
  # Two gaps are equal if they have the same name and
79
83
  # use the same filter.
80
84
  def ==(other)
81
- name == other.name && filter == other.filter
85
+ self.class == other.class &&
86
+ name == other.name &&
87
+ filter == other.filter
82
88
  end
83
89
 
84
90
  # Change the name of the gap.
@@ -107,11 +113,10 @@ class Jig
107
113
  # If _open_ is provided, a single line of text will be wrapped with the _open_
108
114
  # and _close_ strings but multiple lines of text will be formatted as a block
109
115
  # comment. If _close_ is not provided it is taken to be the <i>open.reverse</i>.
110
- # Jig[Jig::Gap.comment] # text reformated to 72 columns
111
- # Jig[Jig::Gap.comment("# ")] # text reformated as Ruby comments
112
- # Jig[Jig::Gap.comment("// ")] # text reformated as Javascript comments
113
- # Jig[Jig::Gap.comment(" *", "/* ")] # text reformated as C comments
114
- # Jig[Jig::Gap.comment(" ", "<-- ", " -->")] # text reformated as XML comments
116
+ # Jig[Jig::Gap.comment] # text reformated to 72 columns
117
+ # Jig[Jig::Gap.comment("# ")] # text reformated as Ruby comments
118
+ # Jig[Jig::Gap.comment("// ")] # text reformated as Javascript comments
119
+ # Jig[Jig::Gap.comment(" *", "/* ")] # text reformated as C comments
115
120
  #
116
121
  # If the default gap name isn't appropriate you must fill in all the arguments:
117
122
  # Jig[Jig::Gap.comment("# ", nil, nil, 72, :alternate)] # alternate gap name
@@ -192,24 +197,44 @@ class Jig
192
197
  #
193
198
  # A.new.secret # NoMethodError
194
199
  # A.new.to_jig.to_s # secret: xyzzy
200
+
201
+ Before = /[^%]*/
202
+ Replace = /%\{(.?)(.+)\1\}/
203
+
195
204
  def parse(string=nil, context=nil)
196
205
  wrapper = context || Module.new.class_eval { binding }
197
- raw = string.scan(/(.*?)(%\{(.)(.*?)\3\}|\z)/).inject([]) { |list, (before, quoted, delim, stripped)|
198
- list << before unless before.empty?
199
- case delim
200
- when ':'
201
- list << stripped.to_sym
202
- when '!'
203
- list << eval("lambda {#{stripped}}", wrapper)
204
- when nil,''
205
- list
206
+ scanner = StringScanner.new(string)
207
+ raw = []
208
+ while !scanner.eos?
209
+ if before = scanner.scan(Before)
210
+ if replace = scanner.scan(Replace)
211
+ raw << before
212
+ raw << interpolate(replace, wrapper)
213
+ else
214
+ raw << before
215
+ raw << scanner.getch unless scanner.eos?
216
+ end
206
217
  else
207
- list << parse_other(delim, stripped)
218
+ raw << scanner.rest
219
+ scanner.terminate
208
220
  end
209
- }
221
+ end
210
222
  Jig.new(*raw)
211
223
  end
212
224
 
225
+ def interpolate(replace, context)
226
+ all, delimiter, content = *(replace.match(Replace))
227
+
228
+ case delimiter
229
+ when ':'
230
+ content.to_sym
231
+ when '!', ''
232
+ eval("lambda {#{content}}", context)
233
+ else
234
+ parse_other(delimiter, content)
235
+ end
236
+ end
237
+
213
238
  def parse_other(delim, stripped)
214
239
  raise ArgumentError, "invalid delimiter: \"#{delim}\""
215
240
  end
@@ -233,7 +258,7 @@ class Jig
233
258
  # puts j # => "i is 1"
234
259
  #
235
260
  # If no arguments are given and no block is given, the jig is constructed
236
- # with a single default gap named <tt>:___</tt> (also known as Jig::GAP).
261
+ # with a single default gap named <tt>:\___</tt> (also known as Jig::GAP).
237
262
  # one_gap = Jig.new
238
263
  # one_gap.gaps # => [:___]
239
264
  def initialize(*items, &block)
@@ -286,6 +311,12 @@ class Jig
286
311
  contents.zip(rawgaps).flatten.eql?(rhs.contents.zip(rhs.rawgaps).flatten)
287
312
  end
288
313
 
314
+ # Construct and array that represents the syntax of the jig including its
315
+ # gaps. Gaps are only distinguished by their name and not their semantics.
316
+ def syntax
317
+ contents.zip(gaps).flatten
318
+ end
319
+
289
320
  # Returns true if +rhs+ is an instance of Jig or one of Jig's subclasses and
290
321
  # the two jigs have equal gap lists and contents (via Array#==).
291
322
  # Procs are not evaluated by Jig#==.
@@ -309,7 +340,7 @@ class Jig
309
340
  end
310
341
 
311
342
  # Returns true if the string representation of the jig matches the
312
- # </tt>rhs.to_str</tt> using String#===. Procs are evaluated by Jig#====.
343
+ # +rhs.to_str+ using String#=~. Procs are evaluated by Jig#===.
313
344
  def ===(rhs)
314
345
  to_s =~ rhs.to_str
315
346
  end
@@ -324,7 +355,7 @@ class Jig
324
355
  # Jig.new(1,:a,2).inspect # => #<Jig: [1, :a, 2]>
325
356
  # Jig.new(Gap.new(:example) { |x| x.to_s.reverse }) # => #<Jig: [:example{}]>
326
357
  def inspect
327
- info = rawgaps.map {|g| g.filter && "#{g.name}{}".to_sym || g.name }
358
+ info = rawgaps.map {|g| g.terse_inspect }
328
359
  "#<Jig: #{contents.zip(info).flatten[0..-2].inspect}>"
329
360
  end
330
361
 
@@ -602,12 +633,12 @@ class Jig
602
633
  dup.after!(*args)
603
634
  end
604
635
 
605
- # call-seq
636
+ # call-seq:
606
637
  # split(pattern=$;, [limit])
607
638
  #
608
639
  # With no arguments, the jig is split at the gap positions into an
609
- # array of strings. If arguments are provided, the entire string is
610
- # rendered to a string and the result of String#split (with the
640
+ # array of strings. If arguments are provided, the jig is
641
+ # rendered to a string by #to_s and the result of String#split (with the
611
642
  # arguments) is returned.
612
643
  def split(*args)
613
644
  if args.empty?
@@ -672,6 +703,7 @@ class Jig
672
703
  alias inspect :to_s
673
704
  undef to_s
674
705
  def to_s; call.to_s; end
706
+ def to_yaml(opts={}); call.to_yaml(opts); end
675
707
  #:startdoc:
676
708
  }
677
709
  contents.last << i
@@ -804,8 +836,10 @@ class Jig
804
836
  gaps.each_with_index do |gap, index|
805
837
  match = index + adjust
806
838
  items = block_given? && yield(gap)
807
- fill = rawgaps.at(match).fill(items)
808
- adjust += plug_gap!(match, fill) - 1
839
+ if items != gap
840
+ fill = rawgaps.at(match).fill(items)
841
+ adjust += plug_gap!(match, fill) - 1
842
+ end
809
843
  end
810
844
  self
811
845
  end
@@ -829,8 +863,8 @@ class Jig
829
863
  end
830
864
 
831
865
  # :stopdoc:
832
- # This method alters the current jig by replacing a gap with a
833
- # (possibly empty) sequence of objects. The contents and rawgap arrays
866
+ # This method alters the current jig by replacing a gap with a (possibly
867
+ # empty) sequence of objects. The contents and rawgap arrays
834
868
  # are modified such that the named gap is removed and the sequence of
835
869
  # objects are put in the logical position of the former gap.
836
870
  #
@@ -229,9 +229,6 @@ class Jig
229
229
  end
230
230
  end
231
231
 
232
- # Returns a standard jig representation of the CSS jig. Gaps that are
233
- # used internally by the CSS class are closed. #to_jig should be used before
234
- # combining one or more CSS jigs.
235
232
  def to_jig
236
233
  Jig.new(plug( :__s => nil, :__de => nil, :__ds => nil ))
237
234
  end
@@ -2,8 +2,8 @@ require 'jig'
2
2
  require 'jig/xml'
3
3
 
4
4
  class Jig
5
- # Jig::XHTML is a subclass of Jig::XML and is designed to assist in the
6
- # construction of XHTML documents.
5
+ # Jig::XHTML is a subclass of Jig::XML and is designed to assist in the
6
+ # construction of XHTML documents.
7
7
  class XHTML < XML
8
8
  attr_accessor :extra
9
9
  protected :extra
@@ -35,8 +35,8 @@ class Jig
35
35
  class <<self
36
36
  # Construct a jig for an XHTML element with _tag as the tag and include
37
37
  # an ID attribute with a guaranteed unique value.
38
- # Example:
39
- # puts Jig::XHTML.element_with_id('div') # <div id="x2354322">\n</div>
38
+ # Example:
39
+ # puts Jig::XHTML.element_with_id('div') # <div id="x2354322">\n</div>
40
40
  def element_with_id(tag, *args)
41
41
  attrs = { 'id' => :id }
42
42
  attrs = attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
@@ -49,11 +49,11 @@ class Jig
49
49
 
50
50
  # Construct a jig for an emtpy XHTML element with _tag as the tag and include
51
51
  # an ID attribute with a guaranteed unique value. The selected id is
52
- # accessible via the eid attribute.
53
- # Example:
54
- # j = Jig::XHTML.element_with_id('input', :name=>'login')
55
- # puts j # <input name="login" id="x2354328"/>
56
- # puts j.eid # x2354328
52
+ # accessible via the eid attribute.
53
+ # Example:
54
+ # j = Jig::XHTML.element_with_id('input', :name=>'login')
55
+ # puts j # <input name="login" id="x2354328"/>
56
+ # puts j.eid # x2354328
57
57
  def element_with_id!(tag, *args)
58
58
  attrs = { 'id' => :id }
59
59
  attrs = attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
@@ -64,10 +64,10 @@ class Jig
64
64
  jig.plug!(:id, jig.eid)
65
65
  end
66
66
 
67
- # Construct an element based on the method name. If the method name
68
- # ends in '_with_id' or '_with_id!', the element is constructed with
69
- # a unique XML id attribute otherwise the Jig::XML element construction
70
- # rules apply.
67
+ # Construct an element based on the method name. If the method name
68
+ # ends in '_with_id' or '_with_id!', the element is constructed with
69
+ # a unique XML id attribute otherwise the Jig::XML element construction
70
+ # rules apply.
71
71
  def method_missing(sym, *args, &block)
72
72
  text = sym.to_s
73
73
  if text.to_s =~ /_with_id!*$/
@@ -83,43 +83,26 @@ class Jig
83
83
  :frameset, %{"-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"}
84
84
  }
85
85
 
86
- # Construct an XHTML DOCTYPE declaration. The first argument, _dtype_, specifies
87
- # the type of document: :strict, :transitional, or :frameset. Any additional
88
- # arguments are rendered after the DOCTYPE declaration. A default gap is *not*
89
- # inserted if their are no arguments. Examples:
90
- #
91
- # puts X.doctype(:strict)
92
- # # => <!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd>
93
- # puts X.doctype(:strict).gaps.size # 0
86
+ # Construct an XHTML DOCTYPE declaration. The first argument, _dtype_, specifies
87
+ # the type of document: :strict, :transitional, or :frameset. Any additional
88
+ # arguments are rendered after the DOCTYPE declaration. A default gap is *not*
89
+ # inserted if their are no arguments. Examples:
90
+ #
91
+ # puts X.doctype(:strict) # <!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd>
92
+ # puts X.doctype(:strict).gaps.size # 0
94
93
  def doctype(dtype, *args, &block)
95
94
  new(%{<!DOCTYPE html PUBLIC #{DOCTYPES.fetch(dtype)}>\n}, *args, &block)
96
95
  end
97
96
 
98
- # call-seq:
99
- # xhtml([attributes]) -> a_jig
100
- # xhtml(doctype, [attributes]) -> a_jig
101
- # xhtml(doctype, *items, [attributes]) -> a_jig
102
- # xhtml(*items, [attributes]) -> a_jig
103
- # Construct a generic XHTML document. With no arguments, a transitional
104
- # document with three gaps, <tt>:title</tt>, <tt>:head</tt>, and <tt>:___</tt>
105
- # is generated. The default gap represents the contents of the body element.
106
- #
107
- # With a single symbol argument, <i>doctype</i>, an XHTMl document is generated
108
- # with a corresponding DOCTYPE declaration (see #doctype).
109
- #
110
- # If any arguments are provided other than a doctype, the additional arguments
111
- # are used as the contents of the HTML element.
112
- #
113
- # A trailing hash argument is assumed to represent additional XML attributes for
114
- # the html element.
115
- # X.xhtml # transitional document with :title, :head, and :___
116
- # X.xhtml :strict # strict document with :title, :head, and :___
117
- # X.xhtml(:strict, head, body) # strict document with explicit head & body
118
- # X.xhtml(head, body) # transitional document with explicit head & body
119
- # X.xhtml :lang => 'jp' # transition document with HTML attributes merged
97
+ # Construct a generic XHTML document. If the first argument is a symbol it is
98
+ # used to look up a matching DOCTYPE declaration (see #doctype).
99
+ # X.xhtml # transitional document with :title, :head, and :___
100
+ # X.xhtml :strict # strict document with :title, :head, and :___
101
+ # X.xhtml :lang => 'jp' # transition document with HTML attributes merged
102
+ # X.xhtml(head, body) # transitional document with explicit head & body
120
103
  def xhtml(dtype=:transitional, *args, &block)
121
- unless Symbol === dtype
122
- dtype,args = :transitional,args.unshift(dtype)
104
+ if dtype.respond_to?(:fetch)
105
+ dtype,*args = :transitional,dtype
123
106
  end
124
107
  attrs = {:lang=>'en', "xml:lang"=>'en', :xmlns=>'http://www.w3.org/1999/xhtml'}
125
108
  attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
@@ -129,11 +112,11 @@ class Jig
129
112
  doctype(dtype,html(*args))
130
113
  end
131
114
 
132
- # A convenience method for constructing an XHTML element with a named
133
- # CSS class and an unique XHTML element ID.
134
- #
135
- # X.container('span', 'urgent', 'This is urgent!')
136
- # => <span class="urgent" id="x2337852"></span>
115
+ # A convenience method for constructing an XHTML element with a named
116
+ # CSS class and an unique XHTML element ID.
117
+ #
118
+ # X.container('span', 'urgent', 'This is urgent!')
119
+ # => <span class="urgent" id="x2337852"></span>
137
120
  def container(tag, css, *args)
138
121
  args.push(Proc.new) if block_given?
139
122
  args.push(:class => css)
@@ -142,26 +125,26 @@ class Jig
142
125
  jig
143
126
  end
144
127
 
145
- # An even shorter way to construct a div container
146
- #
147
- # X.divc('urgent', 'This is urgent!')
148
- # => <div class="urgent" id="x2337852"></div>
128
+ # An even shorter way to construct a div container
129
+ #
130
+ # X.divc('urgent', 'This is urgent!')
131
+ # => <div class="urgent" id="x2337852"></div>
149
132
  def divc(css_class, *args, &block)
150
133
  container(:div, css_class, *args, &block)
151
134
  end
152
135
 
153
- # Generate a link element for a favicon. Extra attributes
154
- # may be specified via the optional argument, _extra_.
155
- #
156
- # X.link_favicon
157
- # => <link src="/favicon.ico" type="image/x-icon" rel="icon"/>
136
+ # Generate a link element for a favicon. Extra attributes
137
+ # may be specified via the optional argument, _extra_.
138
+ #
139
+ # X.link_favicon
140
+ # => <link src="/favicon.ico" type="image/x-icon" rel="icon"/>
158
141
  def link_favicon(extra={})
159
142
  attrs = {:type=>"image/x-icon", :rel=>"icon", :src=>'/favicon.ico'}
160
143
  attrs.merge! extra
161
144
  link!(attrs)
162
145
  end
163
146
 
164
- # XXX: is this no longer needed?
147
+ # XXX: is this no longer needed?
165
148
  def normalize_args(args, attrs={}) # :nodoc"
166
149
  attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
167
150
  args.push(Proc.new) if block_given?
@@ -169,18 +152,18 @@ class Jig
169
152
  return args, attrs
170
153
  end
171
154
 
172
- # Generate a CSS style sheet element. If a 'src' attribute
173
- # is provided the contents is empty. Without a 'src' attribute
174
- # a CDATA block wraps the contents of the element.
175
- #
176
- # j = Jig::XHTML.style
177
- # puts j.plug('/* CSS style sheet */')
178
- #
179
- # <style media="all" type="text/css">
180
- # <![CDATA[
181
- # /* CSS style sheet */
182
- # ]]>
183
- # </style>
155
+ # Generate a CSS style sheet element. If a 'src' attribute
156
+ # is provided the contents is empty. Without a 'src' attribute
157
+ # a CDATA block wraps the contents of the element.
158
+ #
159
+ # j = Jig::XHTML.style
160
+ # puts j.plug('/* CSS style sheet */')
161
+ #
162
+ # <style media="all" type="text/css">
163
+ # <![CDATA[
164
+ # /* CSS style sheet */
165
+ # ]]>
166
+ # </style>
184
167
  def style(*args, &block)
185
168
  attrs = {:type=>"text/css", :media=>"all"}
186
169
  attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
@@ -197,16 +180,16 @@ class Jig
197
180
  link!('rel' => 'stylesheet', 'type' => 'text/css', 'href' => src)
198
181
  end
199
182
 
200
- # Generate a script element. XXX
201
- #
202
- # j = Jig::XHTML.script
203
- # puts j.plug(cdata("# the script"))
204
- #
205
- # <script type=">
206
- # <![CDATA[
207
- # # the script
208
- # ]]>
209
- # </script>
183
+ # Generate a script element. XXX
184
+ #
185
+ # j = Jig::XHTML.script
186
+ # puts j.plug(cdata("# the script"))
187
+ #
188
+ # <script type=">
189
+ # <![CDATA[
190
+ # # the script
191
+ # ]]>
192
+ # </script>
210
193
  def script(*args, &block)
211
194
  attrs = args.pop if args.last.respond_to?(:fetch)
212
195
  args.push(Proc.new) if block_given?
@@ -233,12 +216,12 @@ class Jig
233
216
  new(a({:href=>"#", :onclick => "toggle(#{body.eid})"}, '(details)'), body)
234
217
  end
235
218
 
236
- # Generate a Javascript comment.
237
- #
238
- # j = Jig::XHTML.js_comment
239
- # puts j.plug("comment")
240
- #
241
- # /* comment */
219
+ # Generate a Javascript comment.
220
+ #
221
+ # j = Jig::XHTML.js_comment
222
+ # puts j.plug("comment")
223
+ #
224
+ # /* comment */
242
225
  def js_comment(*args, &block)
243
226
  gap = Jig::Gap.new(:comment) { |*filling|
244
227
  filling.map {|item|
@@ -248,30 +231,30 @@ class Jig
248
231
  new(gap, "\n").plug(:comment, *args)
249
232
  end
250
233
 
251
- # Generate a multiline Javascript comment.
252
- #
253
- # j = Jig::XHTML.js_comments
254
- # puts j.plug("line 1\nline 2")
255
- #
256
- # /*
257
- # line 1
258
- # line 2
259
- # */
234
+ # Generate a multiline Javascript comment.
235
+ #
236
+ # j = Jig::XHTML.js_comments
237
+ # puts j.plug("line 1\nline 2")
238
+ #
239
+ # /*
240
+ # line 1
241
+ # line 2
242
+ # */
260
243
  def js_mlcomment(*args, &block)
261
244
  new("/*\n", new(*args, &block), "\n */\n")
262
245
  end
263
246
 
264
- # Generate an inline script element for javascript.
265
- # The body of the script is wrapped in a CDATA block.
266
- #
267
- # j = Jig::XHTML.javascript
268
- # puts j.plug("// the script")
269
- #
270
- # <script type="text/javascript" language="JavaScript">
271
- # <![CDATA[
272
- # // the script
273
- # ]]>
274
- # </script>
247
+ # Generate an inline script element for javascript.
248
+ # The body of the script is wrapped in a CDATA block.
249
+ #
250
+ # j = Jig::XHTML.javascript
251
+ # puts j.plug("// the script")
252
+ #
253
+ # <script type="text/javascript" language="JavaScript">
254
+ # <![CDATA[
255
+ # // the script
256
+ # ]]>
257
+ # </script>
275
258
  def javascript(*args, &block)
276
259
  attrs = {:type=>"text/javascript", :language=>"JavaScript"}
277
260
  attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
@@ -78,6 +78,26 @@ class Jig
78
78
  j = X.div('inner', :style=> color) # => <div style="color: red">inner</div>
79
79
  j.to_s # => <div style="color: green">inner</div>
80
80
  =end
81
+
82
+ class ALGap < Gap
83
+ def fill(h)
84
+ h && h.map { |k,v| Jig::XML.attribute(k, v) }
85
+ end
86
+ end
87
+
88
+ class AGap < Gap
89
+ attr_accessor :aname
90
+
91
+ def initialize(name, aname)
92
+ super(name)
93
+ self.aname = aname
94
+ end
95
+
96
+ def fill(fill)
97
+ Jig::XML.attribute(aname, fill)
98
+ end
99
+ end
100
+
81
101
  class XML < Jig
82
102
  # Converts _hash_ into attribute value pairs and pushes them
83
103
  # on the end of the jig.
@@ -117,7 +137,8 @@ class Jig
117
137
  when nil, false
118
138
  ""
119
139
  when Symbol
120
- Gap.new(value) { |fill| attribute(aname, fill) }
140
+ AGap.new(value, aname)
141
+ #Gap.new(value) { |fill| attribute(aname, fill) }
121
142
  when Gap
122
143
  value
123
144
  when Proc, Method
@@ -157,7 +178,8 @@ class Jig
157
178
  private :parse_other
158
179
 
159
180
  ATTRS = Gap::ATTRS # :nodoc:
160
- ATTRS_GAP = Gap.new(ATTRS) { |h| h && h.map { |k,v| Jig::XML.attribute(k, v) } } # :nodoc:
181
+ ATTRS_GAP = Jig::ALGap.new(ATTRS)
182
+ #ATTRS_GAP = Gap.new(ATTRS) { |h| h && h.map { |k,v| Jig::XML.attribute(k, v) } } # :nodoc:
161
183
 
162
184
  Element_Cache = {} # :nodoc:
163
185
  # Construct a generic XML element with two gaps:
@@ -295,17 +317,19 @@ class Jig
295
317
  #
296
318
  # \<!-- This is a comment -->
297
319
  def comment(*args)
320
+ #:stopdoc:
298
321
  args.push(lambda{|*x| yield(*x) }) if block_given?
299
322
  args.push GAP if args.empty?
300
323
  jig = (Cache[:comment] ||= new("<!-- ".freeze, GAP, " -->\n".freeze).freeze)
301
324
  jig.plug(GAP, *args)
325
+ #:startdoc:
302
326
  end
303
327
 
304
328
  # Construct a multiline XML comment element.
305
329
  #
306
330
  # Jig::XML.comment("first line\nsecond line")
307
331
  #
308
- # <!--
332
+ # \<!--
309
333
  # first line
310
334
  # second line
311
335
  # -->
@@ -0,0 +1,14 @@
1
+
2
+ module Asserts
3
+ def assert_as_string(expected, jig, message='')
4
+ case expected
5
+ when String
6
+ assert_equal(expected, jig.to_s, message)
7
+ when Regexp
8
+ assert_match(expected, jig.to_s, message)
9
+ end
10
+ end
11
+ def assert_not_similar(a, b, mess="")
12
+ assert_not_match(a, b, mess)
13
+ end
14
+ end
@@ -0,0 +1,110 @@
1
+ # A Rspec file for Jig
2
+ #
3
+ #
4
+ require File.dirname(__FILE__) + '/../lib/jig'
5
+
6
+ context "A null jig" do
7
+ setup do
8
+ @jig = Jig.null
9
+ end
10
+ specify "should be an instance of Jig" do
11
+ @jig.should_be_an_instance_of Jig
12
+ end
13
+ specify "should be full" do
14
+ @jig.should_be_full
15
+ end
16
+ specify "should serialize as the null string" do
17
+ @jig.to_s.should == ""
18
+ end
19
+ specify "should test as null" do
20
+ @jig.should_be_null
21
+ end
22
+ end
23
+
24
+ context "A new jig" do
25
+ setup do
26
+ @jig = Jig.new
27
+ end
28
+ specify "should be an instance of Jig" do
29
+ @jig.should_be_an_instance_of Jig
30
+ end
31
+ specify "should have a default gap" do
32
+ @jig.should_have_gap :___
33
+ end
34
+ specify "should have only one gap" do
35
+ @jig.gap_count.should == 1
36
+ end
37
+ specify "should serialize as the null string" do
38
+ @jig.to_s.should == ""
39
+ end
40
+ specify "should not have a default gap after plug(:alpha)" do
41
+ @jig.plug(Jig::Gap.new(:alpha)).should_not_have_gap :___
42
+ end
43
+ end
44
+
45
+ context "A jig with a single gap named :alpha" do
46
+ setup do
47
+ @jig = Jig.new(:alpha)
48
+ end
49
+ specify 'should have one gap named :alpha' do
50
+ @jig.gap_list.should == [:alpha]
51
+ end
52
+ specify 'should serialize as the null string' do
53
+ @jig.to_s.should == ""
54
+ end
55
+ specify 'should match "beta" when you plug gap :alpha with "beta"' do
56
+ @jig.plug(:alpha, "beta").to_s.should == "beta"
57
+ end
58
+ end
59
+
60
+ context "A jig with two gaps named :alpha" do
61
+ setup do
62
+ @jig = Jig.new(:alpha, :alpha)
63
+ end
64
+ specify 'should have two gaps named :alpha' do
65
+ @jig.gap_list.should == [:alpha, :alpha]
66
+ end
67
+ specify 'should match "hoho" when you plug gap :alpha with "ho"' do
68
+ @jig.plug(:alpha, "ho").to_s.should == "hoho"
69
+ end
70
+ end
71
+
72
+ context "A jig with two gaps named :alpha and :beta" do
73
+ setup do
74
+ @jig = Jig.new(:alpha, :beta)
75
+ end
76
+ specify 'should have two gaps named :alpha, :beta' do
77
+ @jig.gap_list.should == [:alpha, :beta]
78
+ end
79
+ end
80
+
81
+ context "A jig constructed by parsing 'a<:alpha:>b'" do
82
+ setup do
83
+ @jig = Jig.parse "a<:alpha:>b"
84
+ end
85
+ specify 'should have one gap named :alpha' do
86
+ @jig.gap_list.should == [:alpha]
87
+ end
88
+ specify 'should match "ab"' do
89
+ @jig.should_match "ab"
90
+ end
91
+ end
92
+
93
+ context "A jig constructed by parsing 'a<:alpha,beta:>b'" do
94
+ setup do
95
+ Xjig = Class.new(Jig)
96
+ Xjig.enable :xml
97
+ @jig = Xjig.parse "a<:alpha,beta:>b"
98
+ end
99
+ specify 'should have one gap named :beta' do
100
+ @jig.gap_list.should == [:beta]
101
+ end
102
+ specify 'should match "ab"' do
103
+ @jig.should_match "ab"
104
+ end
105
+ specify 'should match "aalpha=1b" after plug :beta with 1' do
106
+ @jig.plug(:beta, 1).should_match "aalpha=1b"
107
+ end
108
+ end
109
+
110
+ # vim: syntax=Ruby
@@ -432,6 +432,13 @@ class MultipleGaps < Test::Unit::TestCase
432
432
  assert_equal("<form onsubmit=\"ab\">\n</form>\n", j2.plug(:gap1, "X").to_s)
433
433
  end
434
434
 
435
+ def xtest_depth
436
+ a = X.new
437
+ b = X.new(:"a/b")
438
+ assert_equal(0, a[Jig::GAP].depth)
439
+ assert_equal(1, b[:"a/b"].depth)
440
+ end
441
+
435
442
  def test_escape
436
443
  ok = 'a'
437
444
  bad = '<'
@@ -0,0 +1,88 @@
1
+
2
+ require 'jig'
3
+ require 'test/jig'
4
+ require 'test/unit'
5
+
6
+ class TestParse < Test::Unit::TestCase
7
+ J = Jig
8
+ include Asserts
9
+ def test_string
10
+ source = "chunky bacon"
11
+ j = Jig.parse(source)
12
+ assert_equal(source, j.to_s)
13
+ end
14
+
15
+ def test_with_newline
16
+ source = "before %{:alpha:} after\n"
17
+ j = Jig.parse(source)
18
+ assert_equal([:alpha], j.gaps)
19
+ assert_equal("before ", j[0].to_s)
20
+ assert_equal(" after\n", j[2].to_s)
21
+ end
22
+
23
+ def test_gap
24
+ source = "chunky %{:adjective:} bacon"
25
+ j = Jig.parse(source)
26
+ assert_equal([:adjective], j.gaps)
27
+ assert_equal("chunky ", j[0].to_s)
28
+ assert_equal(" bacon", j[2].to_s)
29
+ end
30
+
31
+ def test_agap
32
+ source = "<input%{=type,itype=} />"
33
+ j = Jig::XML.parse(source)
34
+ assert_equal([:itype], j.gaps)
35
+ assert_equal("<input", j[0].to_s)
36
+ assert_equal(" />", j[2].to_s)
37
+ jp = j.plug(:itype => 'password')
38
+ assert_equal("<input type=\"password\" />", jp.to_s)
39
+ end
40
+
41
+ def test_agap_error
42
+ source = "<input%{=type=} />"
43
+ assert_raise(ArgumentError) { Jig::XHTML.parse(source) }
44
+ end
45
+
46
+ def test_jig_agap_error
47
+ source = "<input%{=type,itype=} />"
48
+ assert_raise(ArgumentError) { Jig.parse(source) }
49
+ end
50
+
51
+ def test_syntax_error
52
+ source = "%{ , # invalid syntax }"
53
+ assert_raise(ArgumentError) { Jig.parse(source) }
54
+ end
55
+
56
+ def test_lgap
57
+ a = 2
58
+ j = Jig.parse("%{!a + 1!}", binding)
59
+ assert_equal("3", j.to_s)
60
+ end
61
+
62
+ def test_lgap_error
63
+ j = Jig.parse("%{!b + 1!}")
64
+ assert_raise(NameError) { j.to_s }
65
+ end
66
+
67
+ def test_yield
68
+ j = Jig.parse("%{!yield + 1!}") { 5 }
69
+ assert_equal("6", j.to_s)
70
+ end
71
+
72
+ def test_instance_binding
73
+ klass = Class.new {
74
+ def to_jig
75
+ Jig.parse("secret: %{!secret!}", binding)
76
+ end
77
+ def secret
78
+ "xyzzy"
79
+ end
80
+ private :secret
81
+ }
82
+
83
+ instance = klass.new
84
+ assert_raise(NoMethodError) { instance.secret }
85
+ assert_equal("secret: xyzzy", instance.to_jig.to_s)
86
+ end
87
+ end
88
+
data/todo ADDED
@@ -0,0 +1,7 @@
1
+
2
+ Extend Jig#fill to take a list of gaps to consider instead of
3
+ iterating over all the gaps. This will permit a shortcut for
4
+ the special case of plugging a single gap.
5
+
6
+ When plugging gaps via a proc, the proc should only be called
7
+ once for each gap name.
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
2
+ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: jig
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2007-05-17 00:00:00 -07:00
8
- summary: a data structure that supports construction and manipulation of strings with hierarchical structure
6
+ version: 0.1.2
7
+ date: 2008-02-28 00:00:00 -05:00
8
+ summary: A Jig is a data structure that supports construction and manipulation of strings with hierarchical structure.
9
9
  require_paths:
10
10
  - lib
11
11
  email: gwright@rubyforge.org
12
12
  homepage: http://jig.rubyforge.org
13
13
  rubyforge_project: jig
14
- description: "A jig is a data structure designed to facilitate construction and manipulation of strings with an inherent hierarchical syntax. The idea is derived from the <bigwig> project (http://www.brics.dk/bigwig/) and in particular the XML templating constructs described in the paper: A Type System for Dynamic Web Documents (http://www.brics.dk/bigwig/publications/dyndoc.pdf). The name is derived from woodworking where a jig is a template designed to guide other tools. A jig is an ordered sequence of objects (usually strings) and named _gaps_. When rendered as a string by Jig#to_s, the objects are rendered by calling #to_s on each object in order. The gaps are skipped. A new jig may be constructed from an existing jig by 'plugging' one or more of the named gaps. The new jig shares the objects and their ordering from the original jig but with the named gap replaced with the 'plug'. Gaps may be plugged by any object or sequence of objects. When a gap is plugged with another jig, the contents (including gaps) are incorporated into the new jig. In addition to strings and gaps, a Jig may contain a proc object (or lambda or method). Procs within a jig are not evaluated until the jig is rendered as a string by #to_s."
14
+ description: "A jig is an ordered sequence of objects (usually strings) and named _gaps_. When rendered as a string by Jig#to_s, the objects are rendered calling #to_s on each object in order. The gaps are skipped. A new jig may be constructed from an existing jig by 'plugging' one or more of the named gaps. The new jig shares the objects and their ordering from the original jig but with the named gap replaced with the 'plug'. Gaps may be plugged by any object or sequence of objects. When a gap is plugged with another jig, the contents (including gaps) are incorporated into the new jig. Several subclasses (Jig::XML, Jig::XHTML, Jig::CSS) are defined to help in the construction of XML, XHTML, and CSS documents. This is a jig with a single gap named :alpha. Jig.new(:alpha) # => <#Jig: [:alpha]> This is a jig with two objects, 'before' and 'after' separated by a gap named :middle. j = Jig.new('before', :middle, 'after) # => #<Jig: [\"before\", :middle, \"after\"]> The plug operation derives a new jig from the old jig. j.plug(:middle, \", during, and\") # => #<Jig: [\"before\", \", during, and \", \"after\"]> This operation doesn't change j. It can be used again: j.plug(:middle, \" and \") # => #<Jig: [\"before\", \" and \", \"after\"]> There is a destructive version of plug that modifies the jig in place: j.plug!(:middle, \"filled\") # => #<Jig: [\"before\", \"filled\", \"after\"]> j # => #<Jig: [\"before\", \"filled\", \"after\"]> There are a number of ways to construct a Jig and many of them insert an implicit gap into the Jig. This gap is identified as :___ and is used as the default gap for plug operations when one isn't provided:"
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
@@ -25,9 +25,11 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
+ post_install_message:
28
29
  authors:
29
- - Gary Wright
30
+ - Gary R. Wright
30
31
  files:
32
+ - .DS_Store
31
33
  - History.txt
32
34
  - Manifest.txt
33
35
  - README.txt
@@ -36,19 +38,27 @@ files:
36
38
  - lib/jig/css.rb
37
39
  - lib/jig/xhtml.rb
38
40
  - lib/jig/xml.rb
41
+ - test/jig.rb
42
+ - test/jig_spec.rb
39
43
  - test/test_css.rb
40
44
  - test/test_jig.rb
45
+ - test/test_parse.rb
41
46
  - test/test_xhtml.rb
42
47
  - test/test_xml.rb
48
+ - todo
43
49
  test_files:
44
50
  - test/test_css.rb
45
51
  - test/test_jig.rb
52
+ - test/test_parse.rb
46
53
  - test/test_xhtml.rb
47
54
  - test/test_xml.rb
48
- rdoc_options: []
49
-
50
- extra_rdoc_files: []
51
-
55
+ rdoc_options:
56
+ - --main
57
+ - README.txt
58
+ extra_rdoc_files:
59
+ - History.txt
60
+ - Manifest.txt
61
+ - README.txt
52
62
  executables: []
53
63
 
54
64
  extensions: []
@@ -63,5 +73,5 @@ dependencies:
63
73
  requirements:
64
74
  - - ">="
65
75
  - !ruby/object:Gem::Version
66
- version: 1.2.0
76
+ version: 1.4.0
67
77
  version: