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:



130
131
132
133
134
135
136
# File 'lib/sycl.rb', line 130

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:



138
139
140
# File 'lib/sycl.rb', line 138

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


166
167
168
# File 'lib/sycl.rb', line 166

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.



153
154
155
156
157
# File 'lib/sycl.rb', line 153

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.



145
146
147
# File 'lib/sycl.rb', line 145

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

Instance Method Details

#<<(e) ⇒ Object

:nodoc:



183
184
185
186
187
188
# File 'lib/sycl.rb', line 183

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.



175
176
177
178
179
180
181
# File 'lib/sycl.rb', line 175

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:



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

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

#concat(a) ⇒ Object

:nodoc:



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

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

#encode_with(coder) ⇒ Object

:nodoc:



389
390
391
392
# File 'lib/sycl.rb', line 389

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:



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/sycl.rb', line 203

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:



215
216
217
218
219
220
221
222
223
# File 'lib/sycl.rb', line 215

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:



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

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

#method(sym) ⇒ Object

:nodoc:



352
353
354
# File 'lib/sycl.rb', line 352

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

#push(*args) ⇒ Object

:nodoc:



225
226
227
228
229
230
231
232
233
# File 'lib/sycl.rb', line 225

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]'


263
264
265
# File 'lib/sycl.rb', line 263

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.



298
299
300
# File 'lib/sycl.rb', line 298

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).



291
292
293
# File 'lib/sycl.rb', line 291

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]}]'


280
281
282
283
284
# File 'lib/sycl.rb', line 280

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

#replace(a) ⇒ Object

:nodoc:



235
236
237
238
# File 'lib/sycl.rb', line 235

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.



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/sycl.rb', line 366

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:



240
241
242
243
244
245
246
247
248
# File 'lib/sycl.rb', line 240

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:



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

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+$/, '') }


329
330
331
# File 'lib/sycl.rb', line 329

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

#yaml_preprocess!Object

:nodoc:



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

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.



309
310
311
# File 'lib/sycl.rb', line 309

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