Class: Sycl::Array

Inherits:
Array
  • Object
show all
Defined in:
lib/sycl.rb

Overview

A Sycl::Array is like an Array, but creating one from an array blesses any child Array or Hash objects into Sycl::Array or Sycl::Hash objects. All the normal Array methods are supported, and automatically promote any inputs into Sycl equivalents. The following example illustrates this:

h = { 'a' => { 'b' => 'Hello, world!' } }
a = Sycl::Array.new
a << h

puts a.first.a.b   # outputs 'Hello, world!'

A Sycl::Array supports YAML preprocessing and postprocessing, and having individual nodes marked as being rendered in inline style. YAML output is always sorted, unless individual nodes are marked as being rendered unsorted.

a = Sycl::Array.from_array %w{bravo delta charlie alpha}
a.render_inline!
a.yaml_preprocessor { |x| x.each { |e| e.capitalize! } }
a.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }

puts a.first    # outputs 'bravo'
puts a.to_yaml  # outputs '[Alpha, Bravo, Charlie, Delta]'

Defined Under Namespace

Classes: MockNativeType

Constant Summary collapse

@@default_sorting =
true

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Array

:nodoc:



134
135
136
137
138
139
140
# File 'lib/sycl.rb', line 134

def initialize(*args)  # :nodoc:
  @yaml_preprocessor = nil
  @yaml_postprocessor = nil
  @yaml_style = nil
  @render_sorted = @@default_sorting
  super
end

Class Method Details

.[](*args) ⇒ Object

:nodoc:



142
143
144
# File 'lib/sycl.rb', line 142

def self.[](*args)  # :nodoc:
  Sycl::Array.from_array super
end

.default_sorting=(sort) ⇒ Object

Set Default Array Sorting. In some cases we want to instantiate all sycl objects with sorting defaulted to either true or false.

Example:

Sycl::Array.default_sorting = false


170
171
172
# File 'lib/sycl.rb', line 170

def self.default_sorting=(sort)
  @@default_sorting = sort
end

.from_array(array) ⇒ Object

Create a Sycl::Array from a normal Array, or, really, any object that supports Enumerable#each(). Every child Array or Hash gets promoted to a Sycl::Array or Sycl::Hash.



157
158
159
160
161
# File 'lib/sycl.rb', line 157

def self.from_array(array)  # :nodoc:
  retval = Sycl::Array.new
  array.each { |e| retval << Sycl::from_object(e) }
  retval
end

.load_file(filename) ⇒ Object

Like Sycl::load_file(), a shortcut method to create a Sycl::Array from loading and parsing YAML from a file.



149
150
151
# File 'lib/sycl.rb', line 149

def self.load_file(filename)
  Sycl::Array.from_array YAML::load_file filename
end

Instance Method Details

#<<(e) ⇒ Object

:nodoc:



187
188
189
190
191
192
# File 'lib/sycl.rb', line 187

def <<(e)  # :nodoc:
  unless e.is_a?(Sycl::Hash) || e.is_a?(Sycl::Array)
    e = Sycl::from_object(e)
  end
  super
end

#[]=(*args) ⇒ Object

Make sure that if we write to this array, we promote any inputs to their Sycl equivalents. This lets dot notation, styled YAML, and other Sycl goodies continue.



179
180
181
182
183
184
185
# File 'lib/sycl.rb', line 179

def []=(*args)  # :nodoc:
  raise ArgumentError => 'wrong number of arguments' unless args.size > 1
  unless args[-1].is_a?(Sycl::Hash) || args[-1].is_a?(Sycl::Array)
    args[-1] = Sycl::from_object(args[-1])
  end
  super
end

#collect!(&block) ⇒ Object

:nodoc:



194
195
196
# File 'lib/sycl.rb', line 194

def collect!(&block)  # :nodoc:
  super { |o| Sycl::from_object(block.call o) }
end

#concat(a) ⇒ Object

:nodoc:



202
203
204
205
# File 'lib/sycl.rb', line 202

def concat(a)  # :nodoc:
  a = Sycl::Array.from_array(a) unless a.is_a?(Sycl::Array)
  super
end

#encode_with(coder) ⇒ Object

:nodoc:



393
394
395
396
# File 'lib/sycl.rb', line 393

def encode_with(coder)  # :nodoc:
  coder.style = Psych::Nodes::Sequence::FLOW if @yaml_style == :inline
  coder.represent_seq nil, sort
end

#fill(*args, &block) ⇒ Object

:nodoc:



207
208
209
210
211
212
213
214
215
216
217
# File 'lib/sycl.rb', line 207

def fill(*args, &block)  # :nodoc:
  raise ArgumentError => 'wrong number of arguments' if args.empty?
  if block_given?
    super { |idx| Sycl::from_object(block.call idx) }
  else
    unless args[0].is_a?(Sycl::Hash) || args[0].is_a?(Sycl::Array)
      args[0] = Sycl::from_object(args[0])
    end
    super
  end
end

#insert(i, *args) ⇒ Object

:nodoc:



219
220
221
222
223
224
225
226
227
# File 'lib/sycl.rb', line 219

def insert(i, *args)  # :nodoc:
  raise ArgumentError => 'wrong number of arguments' if args.empty?
  args.collect! do |o|
    unless o.is_a?(Sycl::Hash) || o.is_a?(Sycl::Array)
      o = Sycl::from_object(o)
    end
  end
  super
end

#map!(&block) ⇒ Object

:nodoc:



198
199
200
# File 'lib/sycl.rb', line 198

def map!(&block)  # :nodoc:
  super { |o| Sycl::from_object(block.call o) }
end

#method(sym) ⇒ Object

:nodoc:



356
357
358
# File 'lib/sycl.rb', line 356

def method(sym)  # :nodoc:
  sym == :to_yaml ? MockNativeType.new : super
end

#push(*args) ⇒ Object

:nodoc:



229
230
231
232
233
234
235
236
237
# File 'lib/sycl.rb', line 229

def push(*args)  # :nodoc:
  raise ArgumentError => 'wrong number of arguments' if args.empty?
  args.collect! do |o|
    unless o.is_a?(Sycl::Hash) || o.is_a?(Sycl::Array)
      o = Sycl::from_object(o)
    end
  end
  super
end

#render_inline!Object

Make this array, and its children, rendered in inline/flow style. The default is to render arrays in block (multi-line) style.

Example:

a = Sycl::Array::from_array %w{one two}
a.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }

puts a.to_yaml  # output: "- one\n- two"
a.render_inline!
puts a.to_yaml  # output: '[one, two]'


267
268
269
# File 'lib/sycl.rb', line 267

def render_inline!
  @yaml_style = :inline
end

#render_sorted!Object

Sort this array when it is rendered as YAML. Useful when the default_sorting has been set to false and arrays should be sorted.



302
303
304
# File 'lib/sycl.rb', line 302

def render_sorted!
  @render_sorted = true
end

#render_unsorted!Object

Do not sort this array when it is rendered as YAML. Usually we want elements sorted so that diffs are human-readable, however, there are certain cases where array ordering is significant (for example, a sorted list of queues).



295
296
297
# File 'lib/sycl.rb', line 295

def render_unsorted!
  @render_sorted = false
end

#render_values_inline!Object

Keep rendering this array in block (multi-line) style, but, make this array’s children rendered in inline/flow style.

Example:

a = Sycl::Array::from_array ['one', {'two' => ['three']}]
a.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }

a.render_values_inline!
puts a.to_yaml  # output: "- one\n- two: [three]"
a.render_inline!
puts a.to_yaml  # output: '[one, {two: [three]}]'


284
285
286
287
288
# File 'lib/sycl.rb', line 284

def render_values_inline!
  self.each do |e|
    e.render_inline! if e.respond_to?(:render_inline!)
  end
end

#replace(a) ⇒ Object

:nodoc:



239
240
241
242
# File 'lib/sycl.rb', line 239

def replace(a)  # :nodoc:
  a = Sycl::Array.from_array(a) unless a.is_a?(Sycl::Array)
  super
end

#to_yaml(opts = {}) ⇒ Object

Render this object as YAML. Before rendering, run the object through any yaml_preprocessor() code block. After rendering, filter the YAML text through any yaml_postprocessor() code block.

Nodes marked with render_inline!() or render_values_inline!() will be output in flow/inline style, all hashes and arrays will be sorted, and we set a long line width to more or less support line wrap under the Psych library.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/sycl.rb', line 370

def to_yaml(opts = {})
  yaml_preprocess!
  if defined?(YAML::ENGINE) && YAML::ENGINE.yamler == 'psych'
    opts ||= {}
    opts[:line_width] ||= 999999  # Psych doesn't let you disable line wrap
    yaml = super
  else
    yaml = YAML::quick_emit(self, opts) do |out|
      if @render_sorted
        out.seq(nil, @yaml_style || to_yaml_style) do |seq|
          sort.each { |e| seq.add(e) }
        end
      else
        out.seq(nil, @yaml_style || to_yaml_style) do |seq|
          each { |e| seq.add(e) }
        end
      end
    end
  end
  yaml_postprocess yaml
end

#unshift(*args) ⇒ Object

:nodoc:



244
245
246
247
248
249
250
251
252
# File 'lib/sycl.rb', line 244

def unshift(*args)  # :nodoc:
  raise ArgumentError => 'wrong number of arguments' if args.empty?
  args.collect! do |o|
    unless o.is_a?(Sycl::Hash) || o.is_a?(Sycl::Array)
      o = Sycl::from_object(o)
    end
  end
  super
end

#yaml_postprocess(yaml) ⇒ Object

:nodoc:



341
342
343
# File 'lib/sycl.rb', line 341

def yaml_postprocess(yaml)  # :nodoc:
  @yaml_postprocessor ? @yaml_postprocessor.call(yaml) : yaml
end

#yaml_postprocessor(&block) ⇒ Object

Set a postprocessor hook which runs after YML is dumped, for example, via to_yaml() or Sycl::dump(). The hook is a block that gets the YAML text string as an argument, and returns a new, possibly different, YAML text string.

A common example use case is to suppress the initial document separator, which is just visual noise when humans are viewing or editing a single YAML file:

a.yaml_postprocessor { |yaml| yaml.sub(/\A---\s+/, '') }

Your conventions might also prohibit trailing whitespace, which at least the Syck library will tack on the end of YAML hash keys:

a.yaml_postprocessor { |yaml| yaml.gsub(/:\s+$/, '') }


333
334
335
# File 'lib/sycl.rb', line 333

def yaml_postprocessor(&block)
  @yaml_postprocessor = block if block_given?
end

#yaml_preprocess!Object

:nodoc:



337
338
339
# File 'lib/sycl.rb', line 337

def yaml_preprocess!  # :nodoc:
  @yaml_preprocessor.call(self) if @yaml_preprocessor
end

#yaml_preprocessor(&block) ⇒ Object

Set a preprocessor hook which runs before each time YAML is dumped, for example, via to_yaml() or Sycl::dump(). The hook is a block that gets the object itself as an argument. The hook can then set render_inline!() or similar style arguments, prune nil or empty leaf values from hashes, or do whatever other styling needs to be done before a Sycl object is rendered as YAML.



313
314
315
# File 'lib/sycl.rb', line 313

def yaml_preprocessor(&block)
  @yaml_preprocessor = block if block_given?
end