Class: Remap::Compiler

Inherits:
Proxy
  • Object
show all
Extended by:
Catchable
Includes:
Catchable
Defined in:
lib/remap/compiler.rb

Overview

Constructs a Rule from the block passed to Base.define

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Catchable

catch_fatal, catch_ignored

Methods inherited from Proxy

const_missing, #tap

Class Method Details

.call(backtrace: caller, &block) ⇒ Rule

Constructs a rule tree given block

Examples:

Compiles two rules, [get] and [map]

rule = Remap::Compiler.call do
  get :name
  get :age
end

state = Remap::State.call({
  name: "John",
  age: 50
})

error = -> failure { raise failure.exception }

rule.call(state, &error).fetch(:value) # => { name: "John", age: 50 }

Returns:



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/remap/compiler.rb', line 34

def self.call(backtrace: caller, &block)
  unless block
    return Rule::VOID
  end

  rules = new([]).tap do |compiler|
    compiler.instance_exec(&block)
  end.rules

  Rule::Block.new(backtrace: backtrace, rules: rules)
end

Instance Method Details

#allRule::Path::Segment::Quantifier::All

Selects all elements

Examples:

Select all keys in array

rule = Remap::Compiler.call do
  map all, :name, to: :names
end

state = Remap::State.call([
  { name: "John" },
  { name: "Jane" }
])

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { names: ["John", "Jane"] }

Returns:

  • (Rule::Path::Segment::Quantifier::All)


383
384
385
386
387
388
389
# File 'lib/remap/compiler.rb', line 383

def all
  if block_given?
    raise ArgumentError, "all selector does not take a block"
  end

  Selector::All.new(EMPTY_HASH)
end

#at(index) ⇒ Path::Segment::Key

Selects index element in input

Examples:

Select value at index

rule = Remap::Compiler.call do
  map :names, at(1), to: :name
end

state = Remap::State.call({
  names: ["John", "Jane"]
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "Jane" }

Parameters:

  • index (Integer)

Returns:

  • (Path::Segment::Key)

Raises:

  • (ArgumentError)

    if index is not an Integer



464
465
466
467
468
469
470
471
472
473
# File 'lib/remap/compiler.rb', line 464

def at(index)
  if block_given?
    raise ArgumentError, "first selector does not take a block"
  end

  Selector::Index.new(index: index)
rescue Dry::Struct::Error
  raise ArgumentError,
        "Selector at(index) requires an integer argument, got [#{index}] (#{index.class})"
end

#callRule

Returns:



14
# File 'lib/remap/compiler.rb', line 14

delegate :call, to: Compiler

#each(backtrace: caller, &block) ⇒ Rule::Each

Iterates over the input value, passes each value to its block and merges the result back together

Examples:

Map an array of hashes

rule = Remap::Compiler.call do
  each do
    map :name
  end
end

state = Remap::State.call([{
  name: "John"
}, {
  name: "Jane"
}])

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => ["John", "Jane"]

Returns:

  • (Rule::Each)

    ]

Raises:

  • (ArgumentError)

    if no block given



319
320
321
322
323
324
325
# File 'lib/remap/compiler.rb', line 319

def each(backtrace: caller, &block)
  unless block
    raise ArgumentError, "#each requires a block"
  end

  add rule(all, backtrace: backtrace, &block)
end

#embed(mapper, backtrace: caller) ⇒ Rule::Embed

Maps using mapper

Examples:

Embed mapper Car into a person

class Car < Remap::Base
  define do
    map :car do
      map :name, to: :car
    end
  end
end

rule = Remap::Compiler.call do
  map :person do
    embed Car
  end
end

state = Remap::State.call({
  person: {
    car: {
      name: "Volvo"
    }
  }
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { car: "Volvo" }

Parameters:

Returns:

  • (Rule::Embed)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/remap/compiler.rb', line 181

def embed(mapper, backtrace: caller)
  if block_given?
    raise ArgumentError, "#embed does not take a block"
  end

  Types::Mapper[mapper] do
    raise ArgumentError, "Argument to #embed must be a mapper, got #{mapper.class}"
  end

  result = rule(backtrace: backtrace).add do |s0|
    build_embed(s0, mapper, backtrace)
  end

  add result
end

#firstPath::Segment::Key Also known as: any

Selects first element in input

Examples:

Select first value in an array

rule = Remap::Compiler.call do
  map :names, first, to: :name
end

state = Remap::State.call({
  names: ["John", "Jane"]
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "John" }

Returns:

  • (Path::Segment::Key)

    ]



493
494
495
496
497
498
499
# File 'lib/remap/compiler.rb', line 493

def first
  if block_given?
    raise ArgumentError, "first selector does not take a block"
  end

  at(0)
end

#get(*path, backtrace: caller, &block) ⇒ Rule::Map::Required

Select a path and uses the same path as output

Examples:

Map from [:name] to [:name]

rule = Remap::Compiler.call do
  get :name
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "John" }

Parameters:

  • path ([])
    Array<Segment>, Segment

Returns:



118
119
120
# File 'lib/remap/compiler.rb', line 118

def get(*path, backtrace: caller, &block)
  add rule(path, to: path, backtrace: backtrace, &block)
end

#get?(*path, backtrace: caller, &block) ⇒ Rule::Map::Optional

Optional version of #get

Examples:

Map from [:name] to [:name]

rule = Remap::Compiler.call do
  get :name
  get? :age
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "John" }

Returns:

See Also:



143
144
145
# File 'lib/remap/compiler.rb', line 143

def get?(*path, backtrace: caller, &block)
  add rule?(path, to: path, backtrace: backtrace, &block)
end

#lastPath::Segment::Key

Selects last element in input

Examples:

Select last value in an array

rule = Remap::Compiler.call do
  map :names, last, to: :name
end

state = Remap::State.call({
  names: ["John", "Jane", "Linus"]
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "Linus" }

Returns:

  • (Path::Segment::Key)


520
521
522
523
524
525
526
# File 'lib/remap/compiler.rb', line 520

def last
  if block_given?
    raise ArgumentError, "last selector does not take a block"
  end

  at(-1)
end

#map(*path, to: EMPTY_ARRAY, backtrace: caller, &block) ⇒ Rule::Map::Required

Maps input path [input] to output path [to]

Examples:

From path [:name] to [:nickname]

rule = Remap::Compiler.call do
  map :name, to: :nickname
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { nickname: "John" }

Parameters:

  • path ([])
    Array<Segment>, Segment
  • to ([]) (defaults to: EMPTY_ARRAY)
    Array<Symbol>, Symbol

Returns:



67
68
69
# File 'lib/remap/compiler.rb', line 67

def map(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
  add rule(*path, to: to, backtrace: backtrace, &block)
end

#map?(*path, to: EMPTY_ARRAY, backtrace: caller, &block) ⇒ Rule::Map::Optional

Optional version of #map

Examples:

Map an optional field

rule = Remap::Compiler.call do
  to :person do
    map? :age, to: :age
    map :name, to: :name
  end
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { person: { name: "John" } }

Returns:

See Also:



94
95
96
# File 'lib/remap/compiler.rb', line 94

def map?(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
  add rule?(*path, to: to, backtrace: backtrace, &block)
end

#option(id, backtrace: caller) ⇒ Rule::Static::Option

Static option to be selected

Examples:

Set path to option

rule = Remap::Compiler.call do
  set :meaning_of_life, to: option(:number)
end

state = Remap::State.call({}, options: { number: 42 })

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { meaning_of_life: 42 }

Parameters:

  • id (Symbol)

Returns:

  • (Rule::Static::Option)


435
436
437
438
439
440
441
# File 'lib/remap/compiler.rb', line 435

def option(id, backtrace: caller)
  if block_given?
    raise ArgumentError, "option selector does not take a block"
  end

  Static::Option.new(name: id, backtrace: backtrace)
end

#rulesArray<Rule>

Returns:



11
# File 'lib/remap/compiler.rb', line 11

param :rules, type: Types.Array(Rule)

#set(*path, to:, backtrace: caller) ⇒ Rule::Set

Set a static value

Examples:

Set static value to { name: “John” }

rule = Remap::Compiler.call do
  set :name, to: value("John")
end

state = Remap::State.call({})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "John" }

Reference an option

rule = Remap::Compiler.call do
  set :name, to: option(:name)
end

state = Remap::State.call({}, options: { name: "John" })

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { name: "John" }

Parameters:

  • path ([])
    Symbol, Array<Symbol>
  • to (Hash)

    a customizable set of options

Options Hash (to:):

Returns:

  • (Rule::Set)

Raises:

  • (ArgumentError)

    if no path given if path is not a Symbol or Array<Symbol>



232
233
234
235
236
237
238
239
240
241
242
# File 'lib/remap/compiler.rb', line 232

def set(*path, to:, backtrace: caller)
  if block_given?
    raise ArgumentError, "#set does not take a block"
  end

  unless to.is_a?(Static)
    raise ArgumentError, "Argument to #set must be a static value, got #{to.class}"
  end

  add rule(to: path, backtrace: backtrace).add { to.call(_1) }
end

#to(*path, map: EMPTY_ARRAY, backtrace: caller, &block) ⇒ Rule::Map

Maps to path from map with block in between

Examples:

From path [:name] to [:nickname]

rule = Remap::Compiler.call do
  to :nickname, map: :name
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { nickname: "John" }

Parameters:

  • path (Array<Symbol>, Symbol)
  • map (Array<Segment>, Segment) (defaults to: EMPTY_ARRAY)

Returns:



265
266
267
# File 'lib/remap/compiler.rb', line 265

def to(*path, map: EMPTY_ARRAY, backtrace: caller, &block)
  add rule(*map, to: path, backtrace: backtrace, &block)
end

#to?(*path, map: EMPTY_ARRAY, backtrace: caller, &block) ⇒ Rule::Map::Optional

Optional version of #to

Examples:

Map an optional field

rule = Remap::Compiler.call do
  to :person do
    to? :age, map: :age
    to :name, map: :name
  end
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { person: { name: "John" } }

Returns:

See Also:



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

def to?(*path, map: EMPTY_ARRAY, backtrace: caller, &block)
  add rule?(*map, to: path, backtrace: backtrace, &block)
end

#value(value, backtrace: caller) ⇒ Rule::Static::Fixed

Static value to be selected

Examples:

Set path to static value

rule = Remap::Compiler.call do
  set :api_key, to: value("<SECRET>")
end

state = Remap::State.call({})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => { api_key: "<SECRET>" }

Parameters:

  • value (Any)

Returns:

  • (Rule::Static::Fixed)


409
410
411
412
413
414
415
# File 'lib/remap/compiler.rb', line 409

def value(value, backtrace: caller)
  if block_given?
    raise ArgumentError, "option selector does not take a block"
  end

  Static::Fixed.new(value: value, backtrace: backtrace)
end

#wrap(type, backtrace: caller, &block) ⇒ Rule::Wrap

Wraps output in type

Examples:

Wrap an output value in an array

rule = Remap::Compiler.call do
  wrap(:array) do
    map :name
  end
end

state = Remap::State.call({
  name: "John"
})

output = rule.call(state) do |failure|
  raise failure.exception
end

output.fetch(:value) # => ["John"]

Parameters:

  • type (:array)

Yield Returns:

Returns:

  • (Rule::Wrap)

Raises:

  • (ArgumentError)

    if type is not :array



352
353
354
355
356
357
358
359
360
361
362
# File 'lib/remap/compiler.rb', line 352

def wrap(type, backtrace: caller, &block)
  unless block
    raise ArgumentError, "#wrap requires a block"
  end

  unless type == :array
    raise ArgumentError, "Argument to #wrap must equal :array, got [#{type}] (#{type.class})"
  end

  add rule(backtrace: backtrace, &block).then { Array.wrap(_1) }
end