Class: Jig
- Inherits:
-
Object
- Object
- Jig
- Defined in:
- lib/jig.rb,
lib/jig/css.rb,
lib/jig/xml.rb
Overview
A jig is an ordered sequence of objects and named gaps. During construction, a gap is represented by a symbol:
Jig.new('left', :middle, 'right') # => <#Jig: ["left", :middle, "right"]>
Jig.new(1, :two, 3.0, "4") # => <#Jig: [1, :two, 3.0, "4"]>
Jig.new(lambda { rand(6) }) # => <#Jig: [#<Proc:0x00437ee8>]
As a convenience, a block provided to Jig::new is added to the sequence as a proc:
Jig.new { rand(6) } # => #<Jig: [#<Proc:0x00026660@-:2>]>
Jig#[] also constructs new jig instances:
Jig[:header, :body, :footer] # [:header, :body, :footer]
At any time, a jig can be converted to a string by Jig#to_s. The string is constructed by concatenating string representations of the objects in the jig as follows:
-
strings are concatenated as-is
-
gaps are skipped
-
procs are evaluated, the results converted to a string, and concatenated
-
other objects are converted by calling to_s and the resulting string is concatenated
A variety of operations are provided to manipulate jigs but the most common is the ‘plug’ operation:
comment = Jig.new("/*", :comment, "*/")
partial = comment.plug(:comment, "This is a ", :adjective, " comment")
puts partial.plug(:adjective, 'silly') # => /* This is a silly comment */
puts partial.plug(:adjective, 'boring') # => /* This is a boring comment */
The plug method constructs a new jig that shares all the contents of the previous jig but with the named gap replaced by one or more objects. If there are more than one gaps with the same name, they are all replaced with the same sequence of objects.
j = Jig.new("first", :separator, "middle", :separator, "after")
puts j.plug(:separator, '/') # => "first/middle/last"
Defined Under Namespace
Classes: AGap, ALGap, CSS, Gap, XML
Constant Summary collapse
- VERSION =
'0.1.2'
- DEFAULT_GAP =
:section: Construction
Gap.new
- GAP =
DEFAULT_GAP.name
- Before =
Convert a string into a jig. The string is scanned for blocks deliminated by %…. The blocks are interpreted as follows:
%{:identifier:} is converted into a gap named *identifier* %{!code!} is converted to a lambda
Code blocks are interpreted when the resulting jig is rendered via Jig#to_s. Each time parse is called, an anonymous module is created to evaluate all the code blocks created during that call to parse. Alternatively, the code blocks can be evaluated against an explicit binding passed as the second argument.
Jig.parse("abc").to_s # abc Jig.parse("1 %{:x} 3") # Jig[1, :x, 3] Jig.parse("1 %{:x} 3") # Jig[1, :x, 3] a = 5 Jig.parse("%{a + 1}", binding).to_s # 6 Jig.parse("%{b + 1}").to_s # NameError class A def to_jig Jig.parse("secret: %{secret}", binding) end def secret "xyzzy" end private :secret end A.new.secret # NoMethodError A.new.to_jig.to_s # secret: xyzzy
/[^%]*/
- Replace =
/%\{(.?)(.+)\1\}/
Instance Attribute Summary collapse
-
#contents ⇒ Object
readonly
the sequence of objects.
-
#rawgaps ⇒ Object
readonly
the unplugged gaps.
Class Method Summary collapse
- .interpolate(replace, context) ⇒ Object
-
.null ⇒ Object
Construct a null jig.
- .parse(string = nil, context = nil) ⇒ Object
- .parse_other(delim, stripped) ⇒ Object
Instance Method Summary collapse
-
#+(obj) ⇒ Object
(also: #append)
call-seq: jig + obj -> a_jig.
-
#==(rhs) ⇒ Object
Returns true if
rhs
is an instance of Jig or one of Jig’s subclasses and the two jigs have equal gap lists and contents (via Array#==). -
#===(rhs) ⇒ Object
Returns true if the string representation of the jig matches the
rhs.to_str
using String#=~. -
#=~(rhs) ⇒ Object
Returns true if the string representation of the jig matches the
rhs
using String#=~. -
#after(*args) ⇒ Object
call-seq: after(symbol, item, …) -> a_jig after(item, …) -> a_jig.
-
#after!(*args) ⇒ Object
call-seq: after!(symbol, item, …) -> a_jig after!(item, …) -> a_jig.
-
#before(*args) ⇒ Object
call-seq: before(symbol, item, …) -> a_jig before(item, …) -> a_jig.
-
#before!(*args) ⇒ Object
call-seq: before!(symbol, item, …) -> a_jig before!(item, …) -> a_jig.
-
#closed? ⇒ Boolean
Returns true if the jig has no gaps.
-
#concat(collection) ⇒ Object
The collection is converted to a list of items via
*collection
. -
#eql?(rhs) ⇒ Boolean
Returns true if the two jigs are instances of the same class and have equal gap lists and contents (via Array#eql?).
-
#fill! ⇒ Object
Calls the block once for each gap in the jig passing the name of the gap.
-
#filln!(*indices) ⇒ Object
Calls the block once for each index passing the index to the block.
-
#freeze ⇒ Object
Applies Kernel#freeze to the jig and its internal structures.
-
#gaps ⇒ Object
Returns an array containing the names, in order, of the gaps in the current jig.
-
#has_gap?(name) ⇒ Boolean
Returns true if the named gap appears in the jig.
-
#index(name) ⇒ Object
Returns the position of the first gap with the given name or nil if a gap is not found.
-
#initialize(*items, &block) ⇒ Jig
constructor
Construct a jig from the list of items.
-
#initialize_copy(other) ⇒ Object
The internal structure of a jig is duplicated on #dup or #clone, but not the objects that make up the contents of the jig.
-
#inspect ⇒ Object
The inspect string for a jig is an array of objects with gaps represented by symbols.
-
#join(sep = $,) ⇒ Object
The contents of the jig are joined via Array#join.
-
#mult(rhs) ⇒ Object
(also: #*)
call-seq: jig * count -> a_jig jig * array -> a_jig.
-
#null? ⇒ Boolean
Returns true if the jig has no gaps and renders as the empty string.
-
#open? ⇒ Boolean
Returns true if the jig has any gaps.
-
#plug(*args, &block) ⇒ Object
(also: #%)
call-seq: plug -> a_jig plug { |gap| … } -> a_jig plug(hash) -> a_jig plug(symbol, item, …) -> a_jig plug(item, …) -> a_jig.
-
#plug!(*args, &block) ⇒ Object
(also: #[]=, #<<)
call-seq: plug! -> a_jig plug! { |gap| … } -> a_jig plug!(hash) -> a_jig plug!(symbol, *items) -> a_jig plug!(*items) -> a_jig.
-
#plug_gap!(gap, plug) ⇒ Object
:stopdoc: This method alters the current jig by replacing a gap with a (possibly empty) sequence of objects.
-
#plugn(*args, &block) ⇒ Object
call-seq: plugn(n, item) -> a_jig plugn(range, array) -> a_jig plugn(symbol, array) -> a_jig plugn(array) -> a_jig plugn(hash) -> a_jig plugn(item) -> a_jig.
-
#plugn!(first = nil, second = nil, &block) ⇒ Object
Same as #plug but modifies self.
-
#push(*items) ⇒ Object
Pushes the items onto the end of the current jig.
-
#push_jig(other) ⇒ Object
Append a jig onto the end of the current jig.
-
#slice(index, len = 1) ⇒ Object
(also: #[])
call-seq: slice(position) -> jig slice(range) -> jig slice(start, len) -> jig.
-
#split(*args) ⇒ Object
call-seq: split(pattern=$;, [limit]).
-
#syntax ⇒ Object
Construct and array that represents the syntax of the jig including its gaps.
-
#to_jig ⇒ Object
Returns self.
-
#to_s ⇒ Object
(also: #to_str)
A string is constructed by concatenating the contents of the jig.
Constructor Details
#initialize(*items, &block) ⇒ Jig
Construct a jig from the list of items. Symbols in the list are replaced with a gap named by the symbol.
j1 = Jig.new('first', :middle, 'last') # => #<Jig: ['first', :middle, 'last']
j1.gaps # => [:middle]
If a block is provided, it is appended as a proc to the list of items. Procs within a jig are not evaluated until the jig is rendered as a string by to_s.
i = 0
j = Jig.new("i is ") { i }
puts j # => "i is 0"
i = 1
puts j # => "i is 1"
If no arguments are given and no block is given, the jig is constructed with a single default gap named :___
(also known as Jig::GAP).
one_gap = Jig.new
one_gap.gaps # => [:___]
264 265 266 267 268 269 270 |
# File 'lib/jig.rb', line 264 def initialize(*items, &block) @contents = [[]] @rawgaps = [] items.push(block) if block items.push(DEFAULT_GAP) if items.empty? concat(items) end |
Instance Attribute Details
#contents ⇒ Object
the sequence of objects
152 153 154 |
# File 'lib/jig.rb', line 152 def contents @contents end |
#rawgaps ⇒ Object
the unplugged gaps
155 156 157 |
# File 'lib/jig.rb', line 155 def rawgaps @rawgaps end |
Class Method Details
.interpolate(replace, context) ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/jig.rb', line 225 def interpolate(replace, context) all, delimiter, content = *(replace.match(Replace)) case delimiter when ':' content.to_sym when '!', '' eval("lambda {#{content}}", context) else parse_other(delimiter, content) end end |
.null ⇒ Object
166 167 168 |
# File 'lib/jig.rb', line 166 def null new(nil) end |
.parse(string = nil, context = nil) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/jig.rb', line 204 def parse(string=nil, context=nil) wrapper = context || Module.new.class_eval { binding } scanner = StringScanner.new(string) raw = [] while !scanner.eos? if before = scanner.scan(Before) if replace = scanner.scan(Replace) raw << before raw << interpolate(replace, wrapper) else raw << before raw << scanner.getch unless scanner.eos? end else raw << scanner.rest scanner.terminate end end Jig.new(*raw) end |
.parse_other(delim, stripped) ⇒ Object
238 239 240 |
# File 'lib/jig.rb', line 238 def parse_other(delim, stripped) raise ArgumentError, "invalid delimiter: \"#{delim}\"" end |
Instance Method Details
#+(obj) ⇒ Object Also known as: append
call-seq:
jig + obj -> a_jig
Duplicate the current jig then use concat to add obj.
j = Jig[1, :alpha]
j + 2 # Jig[1, :alpha, 2]
j + :beta # Jig[1, :alpha, :beta]
j + Jig[:beta] # Jig[1, :alpha, :beta]
j + [3,4] # Jig[1, :alpha, 3, 4]
j + [Jig.new, Jig.new] # Jig[1, :alpha, :___, :___]
j + Jig[:beta] * 2 # Jig[1, :alpha, :beta, :beta]
933 934 935 |
# File 'lib/jig.rb', line 933 def +(obj) dup.concat(obj) end |
#==(rhs) ⇒ Object
Returns true if rhs
is an instance of Jig or one of Jig’s subclasses and the two jigs have equal gap lists and contents (via Array#==). Procs are not evaluated by Jig#==.
323 324 325 326 |
# File 'lib/jig.rb', line 323 def ==(rhs) Jig === rhs && contents.zip(rawgaps).flatten == rhs.contents.zip(rhs.rawgaps).flatten end |
#===(rhs) ⇒ Object
Returns true if the string representation of the jig matches the rhs.to_str
using String#=~. Procs are evaluated by Jig#===.
344 345 346 |
# File 'lib/jig.rb', line 344 def ===(rhs) to_s =~ rhs.to_str end |
#=~(rhs) ⇒ Object
Returns true if the string representation of the jig matches the rhs
using String#=~. Procs are evaluated by Jig#=~.
Jig.new("chitchat") =~ Jig.new("chit", "chat") # => true
Jig.new("chitchat") =~ Jig.new("chit", :gap, "chat") # => true
Jig.new("chitchat") =~ /chit/ # => true
Jig.new(1,:a,2) =~ Jig.new(1, 2, :a) # => true
334 335 336 337 338 339 340 |
# File 'lib/jig.rb', line 334 def =~(rhs) if Regexp === rhs to_s =~ rhs else to_s == rhs.to_s end end |
#after(*args) ⇒ Object
call-seq:
after(symbol, item, ...) -> a_jig
after(item, ...) -> a_jig
A new jig is constructed by inserting the items after the specified gap or the default gap if the first argument is not a symbol. The gap itself remains in the new jig.
Jig.new.after(1,2,3) # => #<Jig: [:___, 1, 2, 3]>
Jig.new.after(:a, 1,2,3) # => #<Jig: [:___]>
Jig.new(:a, :b).after(:a, 1) # => #<Jig: [:a, 1, :b]>
632 633 634 |
# File 'lib/jig.rb', line 632 def after(*args) dup.after!(*args) end |
#after!(*args) ⇒ Object
call-seq:
after!(symbol, item, ...) -> a_jig
after!(item, ...) -> a_jig
Like #after but modifies the current jig.
806 807 808 809 810 811 812 813 814 815 816 817 |
# File 'lib/jig.rb', line 806 def after!(*args) if Symbol === args.first gap = args.shift else gap = GAP end if current = rawgaps.find {|x| x.name == gap} plug!(gap, args.unshift(current)) else self end end |
#before(*args) ⇒ Object
call-seq:
before(symbol, item, ...) -> a_jig
before(item, ...) -> a_jig
Returns a new jig constructed by inserting the item before the specified gap or the default gap if the first argument is not a symbol. The gap itself remains in the new jig.
Jig.new.before(1,2,3) # => #<Jig: [1, 2, 3, :___]>
Jig.new.before(:a, 1,2,3) # => #<Jig: [:___]>
Jig.new(:a, :b).before(:b, 1) # => #<Jig: [:a, 1, :b]>
618 619 620 |
# File 'lib/jig.rb', line 618 def before(*args) dup.before!(*args) end |
#before!(*args) ⇒ Object
call-seq:
before!(symbol, item, ...) -> a_jig
before!(item, ...) -> a_jig
Like #before but modifies the current jig.
788 789 790 791 792 793 794 795 796 797 798 799 |
# File 'lib/jig.rb', line 788 def before!(*args) if Symbol === args.first gap = args.shift else gap = GAP end if current = rawgaps.find {|x| x.name == gap} plug!(gap, args.push(current)) else self end end |
#closed? ⇒ Boolean
366 367 368 |
# File 'lib/jig.rb', line 366 def closed? rawgaps.empty? end |
#concat(collection) ⇒ Object
729 730 731 |
# File 'lib/jig.rb', line 729 def concat(collection) push(*collection) end |
#eql?(rhs) ⇒ Boolean
Returns true if the two jigs are instances of the same class and have equal gap lists and contents (via Array#eql?). Jigs that are not equal may still have the same string representation. Procs are not evaluated by Jig#eql?.
309 310 311 312 |
# File 'lib/jig.rb', line 309 def eql?(rhs) self.class == rhs.class && contents.zip(rawgaps).flatten.eql?(rhs.contents.zip(rhs.rawgaps).flatten) end |
#fill! ⇒ Object
Calls the block once for each gap in the jig passing the name of the gap. If the block returns the gapname, the gap remains in the jig, otherwise the gap is replaced with the return value of the block. If called without a block, all the gaps are replaced with the empty string.
834 835 836 837 838 839 840 841 842 843 844 845 |
# File 'lib/jig.rb', line 834 def fill! adjust = 0 gaps.each_with_index do |gap, index| match = index + adjust items = block_given? && yield(gap) if items != gap fill = rawgaps.at(match).fill(items) adjust += plug_gap!(match, fill) - 1 end end self end |
#filln!(*indices) ⇒ Object
Calls the block once for each index passing the index to the block. The gap is replaced with the return value of the block. If called without a block, the indexed gaps are replaced with the empty string.
851 852 853 854 855 856 857 858 859 860 861 862 863 |
# File 'lib/jig.rb', line 851 def filln!(*indices) # XXX need to handle indices that are too small adjust = 0 normalized = indices.map { |x| (x >= 0) && x || (x+rawgaps.size) }.sort normalized.each do |index| match = index + adjust gap = rawgaps.fetch(match) items = block_given? && yield(index) fill = gap.fill(items) adjust += plug_gap!(match, fill) - 1 end self end |
#freeze ⇒ Object
Applies Kernel#freeze to the jig and its internal structures. A frozen jig may still be used with non-mutating methods such as #plug but an exception will be raised if a mutating method such as #push or #plug! are called.
659 660 661 662 663 664 |
# File 'lib/jig.rb', line 659 def freeze super @contents.freeze @rawgaps.freeze self end |
#gaps ⇒ Object
392 393 394 |
# File 'lib/jig.rb', line 392 def gaps rawgaps.map { |g| g.name } end |
#has_gap?(name) ⇒ Boolean
399 400 401 |
# File 'lib/jig.rb', line 399 def has_gap?(name) rawgaps.find {|g| g.name == name } end |
#index(name) ⇒ Object
409 410 411 412 |
# File 'lib/jig.rb', line 409 def index(name) rawgaps.each_with_index {|g,i| return (i*2)+1 if g.name == name } nil end |
#initialize_copy(other) ⇒ Object
The internal structure of a jig is duplicated on #dup or #clone, but not the objects that make up the contents of the jig. This is analogous to how an array is duplicated.
275 276 277 278 279 |
# File 'lib/jig.rb', line 275 def initialize_copy(other) super @contents = other.contents.dup @rawgaps = other.rawgaps.dup end |
#inspect ⇒ Object
The inspect string for a jig is an array of objects with gaps represented by symbols. Gaps with associated filters are shown with trailing braces (:gap{}).
Jig.new.inspect # => #<Jig: [:___]>
Jig.new(1,:a,2).inspect # => #<Jig: [1, :a, 2]>
Jig.new(Gap.new(:example) { |x| x.to_s.reverse }) # => #<Jig: [:example{}]>
357 358 359 360 |
# File 'lib/jig.rb', line 357 def inspect info = rawgaps.map {|g| g.terse_inspect } "#<Jig: #{contents.zip(info).flatten[0..-2].inspect}>" end |
#join(sep = $,) ⇒ Object
The contents of the jig are joined via Array#join.
652 653 654 |
# File 'lib/jig.rb', line 652 def join(sep=$,) contents.join(sep) end |
#mult(rhs) ⇒ Object Also known as: *
call-seq:
jig * count -> a_jig
jig * array -> a_jig
With an integer argument, a new jig is constructed by concatenating count copies of self.
three = Jig.new * 3 # => Jig[:___, :___, :___]
puts three.plug('3') # => "333"
With an array argument, the elements of the array are used to plug the default gap. The resulting jigs are concatenated to form the final result:
require 'yaml'
item = Jig["- ", :___, "\n"] # => #<Jig: ["- ", :___, "\n"]>
list = item * [1,2,3] # => #<Jig: ["- ", 1, "\n", "- ", 2, "\n", "- ", 3, "\n"]>
puts list # => "- 1\n- 2\n- 3\n"
puts YAML.load(list.to_s) # => [1, 2, 3]
512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/jig.rb', line 512 def mult(rhs) case rhs when Integer raise ArgumentError, "count must be greater than zero" if rhs < 1 (1...rhs).inject(dup) { |j,i| j.push(self) } when Array rhs.inject(Jig.null) { |j,x| j.concat( plug(x) ) } else raise ArgumentError, "rhs operand for * must be Integer or Array, was #{rhs.class})" end end |
#null? ⇒ Boolean
383 384 385 |
# File 'lib/jig.rb', line 383 def null? closed? && to_s.empty? end |
#open? ⇒ Boolean
374 375 376 |
# File 'lib/jig.rb', line 374 def open? not rawgaps.empty? end |
#plug(*args, &block) ⇒ Object Also known as: %
call-seq:
plug -> a_jig
plug { |gap| ... } -> a_jig
plug(hash) -> a_jig
plug(symbol, item, ...) -> a_jig
plug(item, ...) -> a_jig
Duplicates the current jig, plugs one or more named gaps, and returns the result. Plug silently ignores attempts to fill undefined gaps. In all cases, the replacement items are inserted into the jig as during Jig construction (see Jig#new).
If called with no arguments, any remaining gaps are plugged with nil.
If called with a block, the name of each gap in the jig is passed to the block and the gap is replaced with the return value of the block.
If called with a hash, the keys are used as gap names and the values are used to plug the respective gaps. The gaps are effectively plugged in parallel to avoid any ambiguity when gaps are plugged with jigs that themselves contain additional gaps.
If called with a single symbol argument, the default gap is replaced with a new gap named by the symbol.
If two or more arguments are provided and the first argument is a symbol, the named gap is replaced with the list of items.
In all other cases, the default gap is replaced with the list of items.
j = Jig.new # => #<Jig: [:___]>
jg = Jig[:gamma, :epsilon] # => #<Jig: [:gamma, :epsilon]>
j.plug :alpha # => #<Jig: [:alpha]>
j.plug 1 # => #<Jig: [1]>
j.plug :alpha, 'a' # => #<Jig: ['a']>
jg.plug :gamma, ['a', 'b'] # => #<Jig: ['a', 'b', :epsilon]>
jg.plug :gamma => 'a',
:epsilon => 'e' # => #<Jig: ['a', 'e']>
j.plug [1,2,3] # => #<Jig: [1, 2, 3]>
j.plug # => #<Jig: []>
568 569 570 |
# File 'lib/jig.rb', line 568 def plug(*args, &block) dup.plug!(*args, &block) end |
#plug!(*args, &block) ⇒ Object Also known as: []=, <<
call-seq:
plug! -> a_jig
plug! { |gap| ... } -> a_jig
plug!(hash) -> a_jig
plug!(symbol, *items) -> a_jig
plug!(*items) -> a_jig
Plugs one or more named gaps (see #plug) and returns self. The current jig is modified. To construct a new jig use #plug instead. If the named plug is not defined, the jig is not changed.
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 |
# File 'lib/jig.rb', line 743 def plug!(*args, &block) return fill!(&block) if block or args.empty? first, *more = args case first when Hash fill! { |g| first.fetch(g, g) } when Symbol if more.empty? fill! { |g| g == GAP ? first : g } else fill! { |g| g == first ? (x = *more) : g } end else fill! { |g| g == GAP ? (x = *args) : g } end end |
#plug_gap!(gap, plug) ⇒ Object
:stopdoc: This method alters the current jig by replacing a gap with a (possibly empty) sequence of objects. The contents and rawgap arrays are modified such that the named gap is removed and the sequence of objects are put in the logical position of the former gap.
Gaps and contents are maintainted in two separate arrays. Each element in the contents array is a list of objects implemented as an array. The first element of the gap array represents the gap between the the first and second element of the contents array.
+----+----+
| | | <--- rawgaps array
+----+----+
+----+----+----+
| | | | <--- contents array
+----+----+----+
The following relation always holds: rawgaps.size == contents.size - 1 :startdoc: Replaces the named gap in the current jig with plug and returns the number of gaps that were inserted in its place.
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 |
# File 'lib/jig.rb', line 887 def plug_gap!(gap, plug) case plug when String contents[gap,2] = [contents[gap] + [plug] + contents[gap+1]] rawgaps.delete_at(gap) return 0 when nil, [] contents[gap,2] = [contents[gap] + contents[gap+1]] rawgaps.delete_at(gap) return 0 else plug = Jig[*plug] unless Jig === plug filling, gaps = plug.contents, plug.rawgaps end case filling.size when 0 contents[gap,2] = [contents[gap] + contents[gap+1]] when 1 contents[gap,2] = [contents[gap] + filling.first + contents[gap+1]] else contents[gap,2] = [contents[gap] + filling.first] + filling[1..-2] + [filling.last + contents[gap+1]] end rawgaps[gap, 1] = gaps gaps.size end |
#plugn(*args, &block) ⇒ Object
call-seq:
plugn(n, item) -> a_jig
plugn(range, array) -> a_jig
plugn(symbol, array) -> a_jig
plugn(array) -> a_jig
plugn(hash) -> a_jig
plugn(item) -> a_jig
Similar to #plug but gaps are identified by an integer offset, not by name. Unlike #index, and #slice, #plugn assumes that gaps are indexed consecutively starting with 0.
-
When the first argument is an integer,
n
, the n-th gap is replaced with the item. -
When the first argument is a range, the gaps indexed by
range
are replaced with the items inarray
. -
When the only argument is an array, the gaps indexed by
0...array.size
are replaced with the items in the array. -
When the only argument is a hash, the keys of the hash are taken as indices and the respective gaps are replaced with the associated values from the hash.
-
Any other single argument is taken as the replacement for the first gap.
Examples:
list = Jig[:item, ',', :item, ',', :item]
list.plugn(1, 'second') # => ",second,"
list.plugn(1..2, %w{second third}) # => ",second,third"
list.plugn('first') # => "first,,"
list.plugn(%w{first second}) # => "first,second,"
list.plugn(0 => 'first', 2 => 'third') # => "first,,third"
604 605 606 |
# File 'lib/jig.rb', line 604 def plugn(*args, &block) dup.plugn!(*args, &block) end |
#plugn!(first = nil, second = nil, &block) ⇒ Object
Same as #plug but modifies self.
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
# File 'lib/jig.rb', line 763 def plugn!(first=nil, second=nil, &block) return filln!(&block) if block or !first case first when Hash filln!(*first.keys) { |i| first[i] } when Integer #filln!(first => second) filln!(first) { second } when Array filln!(*(0...first.size)) { |index| first.fetch(index) } when Range # pairs = first.inject({}) { |p, i| p[i] = second[i-first.begin]; p } filln!(*first) { |index| second && second.fetch(index-first.begin) } else filln!(0) { first } end end |
#push(*items) ⇒ Object
Pushes the items onto the end of the current jig. The current jig is modified. Use jig.dup.push(*items) if you want a fresh jig. Individual items are handled as follows:
-
strings: pushed as is
-
symbols: converted to a gap and pushed
-
gaps: pushed as is
-
jigs: each item of the other jig is pushed in order to the current jig, including gaps.
-
any object that responds to to_jig is converted and the results pushed.
-
any object that responds to call is pushed as a proc.
-
all other objects are pushed as is.
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
# File 'lib/jig.rb', line 679 def push(*items) items.each do |i| case i when String then contents.last << i when Symbol then rawgaps << Gap.new(i) contents << [] when Jig then push_jig i when NilClass, FalseClass then next when Jig::Gap then rawgaps << i contents << [] else if respond_to?(p = "push_#{i.class.name.downcase}") send(p, i) elsif i.respond_to? :to_jig push_jig i.to_jig elsif i.respond_to? :call (class <<i; self; end).class_eval { undef inspect #:stopdoc: alias inspect :to_s undef to_s def to_s; call.to_s; end def to_yaml(opts={}); call.to_yaml(opts); end #:startdoc: } contents.last << i else contents.last << i end end #contents.last.concat(add) end self end |
#push_jig(other) ⇒ Object
Append a jig onto the end of the current jig.
916 917 918 919 920 |
# File 'lib/jig.rb', line 916 def push_jig(other) self.contents = contents[0..-2] + [contents[-1] + other.contents[0]] + other.contents[1..-1] rawgaps.concat other.rawgaps self end |
#slice(index, len = 1) ⇒ Object Also known as: []
call-seq:
slice(position) -> jig
slice(range) -> jig
slice(start, len) -> jig
Extracts parts of a jig. The indexing scheme for jigs accounts for contents and gaps as follows:
1 3 <- gaps
+----+----+----+
| | | |
+----+----+----+
0 2 4 <- contents
Each indexible element of the contents is itself a list of zero or more objects. A jig with n gaps will always have n + 1 content lists.
When called with a single integer (pos), slice returns the indexed item (a gap or a content list) as a jig.
j = Jig.new(0, :alpha, 'z')
j.slice(0) # => #<Jig: [0]>
j.slice(1) # => #<Jig: [:alpha]>
j.slice(2) # => #<Jig: ['z']>
When called with a range or a start position and length, slice extracts the indexed items and returns them as a new jig.
j = Jig.new(0, :alpha, 'z')
j.slice(0..1) # => #<Jig: [0, :alpha]>
j.slice(1..2) # => #<Jig: [:alpha, 'z']>
j.slice(0,1) # => #<Jig: [0]>
j.slice(0,2) # => #<Jig: [0, :alpha]>
Negative array indices are respected:
j = Jig.new(0, :alpha, 'z')
j.slice(-1) # => #<Jig: ['z']>
j.slice(-2..-1) # => #<Jig: [:alpha, 'z']>
j.slice(-2, 1) # => #<Jig: [:alpha]>
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/jig.rb', line 466 def slice(index, len=1) if Range === index if index.exclude_end? first, last = index.begin, index.end - 1 else first, last = index.begin, index.end end else first, last = index, index + len - 1 end # Adjust for negative indices. first = 2*contents.size + first - 1 if first < 0 last = 2*contents.size + last - 1 if last < 0 first_adjust, last_adjust = first % 2, last % 2 j = Jig.new j.rawgaps = rawgaps[((first - first_adjust)/2)...((last + last_adjust)/2)] j.contents = contents[((first + first_adjust)/2)..((last - last_adjust)/2)] j.contents.unshift([]) if first_adjust.nonzero? j.contents.push([]) if last_adjust.nonzero? if !j.contents or !j.rawgaps raise ArgumentError, "index #{index} out of range" end j end |
#split(*args) ⇒ Object
call-seq:
split(pattern=$;, [limit])
With no arguments, the jig is split at the gap positions into an array of strings. If arguments are provided, the jig is rendered to a string by #to_s and the result of String#split (with the arguments) is returned.
643 644 645 646 647 648 649 |
# File 'lib/jig.rb', line 643 def split(*args) if args.empty? contents.map { |c| c.join } else to_s.split(*args) end end |
#syntax ⇒ Object
Construct and array that represents the syntax of the jig including its gaps. Gaps are only distinguished by their name and not their semantics.
316 317 318 |
# File 'lib/jig.rb', line 316 def syntax contents.zip(gaps).flatten end |
#to_jig ⇒ Object
417 418 419 |
# File 'lib/jig.rb', line 417 def to_jig self end |
#to_s ⇒ Object Also known as: to_str
A string is constructed by concatenating the contents of the jig. Gaps are effectively considered null strings. Any procs in the jig are evaluated, the results converted to a string via to_s. All other objects are converted to strings via to_s.
823 824 825 |
# File 'lib/jig.rb', line 823 def to_s contents.flatten.join end |