class ROM::Changeset::Stateful

Stateful changesets carry data and can transform it into a different structure compatible with a persistence backend

@abstract

Constants

EMPTY_PIPE

Default no-op pipe

Public Class Methods

default_pipe(context) click to toggle source

Build default pipe object

This can be overridden in a custom changeset subclass

@return [Pipe]

# File lib/rom/changeset/stateful.rb, line 89
def self.default_pipe(context)
  !pipes.empty? ? pipes.map { |p| p.bind(context) }.reduce(:>>) : EMPTY_PIPE
end
extend(*, &block) click to toggle source

Define a changeset mapping excluded from diffs

@see Changeset::Stateful.map @see Changeset::Stateful#extend

@return [Array<Pipe>, Transproc::Function>]

@api public

Calls superclass method
# File lib/rom/changeset/stateful.rb, line 76
def self.extend(*, &block)
  if block
    map(use_for_diff: false, &block)
  else
    super
  end
end
inherited(klass) click to toggle source

@api private

Calls superclass method
# File lib/rom/changeset/stateful.rb, line 94
def self.inherited(klass)
  return if klass == ROM::Changeset

  super
  klass.instance_variable_set(:@__pipes__, pipes.dup)
end
map(**options, &block) click to toggle source

Define a changeset mapping

Subsequent mapping definitions will be composed together and applied in the order they way defined

@example Transformation DSL

class NewUser < ROM::Changeset::Create
  map do
    unwrap :address, prefix: true
  end
end

@example Using custom block

class NewUser < ROM::Changeset::Create
  map do |tuple|
    tuple.merge(created_at: Time.now)
  end
end

@example Multiple mappings (executed in the order of definition)

class NewUser < ROM::Changeset::Create
  map do
    unwrap :address, prefix: true
  end

  map do |tuple|
    tuple.merge(created_at: Time.now)
  end
end

@return [Array<Pipe>, Transproc::Function>]

@see github.com/solnic/transproc Transproc

@api public

# File lib/rom/changeset/stateful.rb, line 60
def self.map(**options, &block)
  if block.parameters.empty?
    pipes << Class.new(Pipe).define!(&block).new(**options)
  else
    pipes << Pipe.new(block, **options)
  end
end
pipes() click to toggle source

@api private

# File lib/rom/changeset/stateful.rb, line 102
def self.pipes
  @__pipes__
end

Public Instance Methods

associate(other, name = Associated.infer_assoc_name(other)) click to toggle source

Associate a changeset with another changeset or hash-like object

@example with another changeset

new_user = users.changeset(name: 'Jane')
new_task = users.changeset(:tasks, title: 'A task')

new_task.associate(new_user, :users)

@example with a hash-like object

user = users.users.by_pk(1).one
new_task = users.changeset(:tasks, title: 'A task')

new_task.associate(user, :users)

@param [#to_hash, Changeset] other Other changeset or hash-like object @param [Symbol] name The association identifier from schema

@api public

# File lib/rom/changeset/stateful.rb, line 225
def associate(other, name = Associated.infer_assoc_name(other))
  Associated.new(self, associations: { name => other })
end
command_compiler_options() click to toggle source

@api private

Calls superclass method ROM::Changeset#command_compiler_options
# File lib/rom/changeset/stateful.rb, line 248
def command_compiler_options
  super.merge(result: result)
end
commit() click to toggle source

Commit stateful changeset

@see Changeset#commit

@api public

# File lib/rom/changeset/stateful.rb, line 203
def commit
  command.call(self)
end
data(data) click to toggle source

Return changeset with data

@param [Hash] data

@return [Changeset]

@api public

# File lib/rom/changeset/stateful.rb, line 170
def data(data)
  with(__data__: data)
end
extend(*steps, **options, &block) click to toggle source

Pipe changeset's data using custom steps define on the pipe. You should use map instead except updating timestamp fields. Calling changeset.extend builds a pipe that excludes certain steps for generating the diff. Currently the only place where it is used is update changesets with the `:touch` step, i.e. `changeset.extend(:touch).diff` will exclude `:updated_at` from the diff.

@see Changeset::Stateful#map

@return [Changeset]

@api public

# File lib/rom/changeset/stateful.rb, line 151
def extend(*steps, **options, &block)
  if block
    if !steps.empty?
      extend(*steps, **options).extend(**options, &block)
    else
      with(pipe: pipe.compose(Pipe.new(block).bind(self), **options))
    end
  else
    with(pipe: steps.reduce(pipe.with(**options)) { |a, e| a.compose(pipe[e], **options) })
  end
end
inspect() click to toggle source

Return string representation of the changeset

@return [String]

@api public

# File lib/rom/changeset/stateful.rb, line 243
def inspect
  %(#<#{self.class} relation=#{relation.name.inspect} data=#{__data__}>)
end
map(*steps, &block) click to toggle source

Pipe changeset's data using custom steps define on the pipe

@overload map(*steps)

Apply mapping using built-in transformations

@example
  changeset.map(:add_timestamps)

@param [Array<Symbol>] steps A list of mapping steps

@overload map(&block)

Apply mapping using a custom block

@example
  changeset.map { |tuple| tuple.merge(created_at: Time.now) }

@overload map(*steps, &block)

Apply mapping using built-in transformations and a custom block

@example
  changeset.map(:add_timestamps) { |tuple| tuple.merge(status: 'published') }

@param [Array<Symbol>] steps A list of mapping steps

@return [Changeset]

@api public

# File lib/rom/changeset/stateful.rb, line 134
def map(*steps, &block)
  extend(*steps, for_diff: true, &block)
end
pipe() click to toggle source

Data transformation pipe

@return [Changeset::Pipe]

@api private

# File lib/rom/changeset/stateful.rb, line 257
def pipe
  @pipe ||= self.class.default_pipe(self)
end
result() click to toggle source

Return command result type

@return [Symbol]

@api private

# File lib/rom/changeset/stateful.rb, line 234
def result
  __data__.is_a?(Array) ? :many : :one
end
to_a() click to toggle source

Coerce changeset to an array

This will send the data through the pipe

@return [Array]

@api public

# File lib/rom/changeset/stateful.rb, line 193
def to_a
  result == :one ? [to_h] : __data__.map { |element| pipe.call(element) }
end
Also aliased as: to_ary
to_ary()
Alias for: to_a
to_h() click to toggle source

Coerce changeset to a hash

This will send the data through the pipe

@return [Hash]

@api public

# File lib/rom/changeset/stateful.rb, line 181
def to_h
  pipe.call(__data__)
end
Also aliased as: to_hash
to_hash()
Alias for: to_h

Private Instance Methods

method_missing(meth, *args, &block) click to toggle source

@api private

Calls superclass method
# File lib/rom/changeset/stateful.rb, line 269
def method_missing(meth, *args, &block)
  if __data__.respond_to?(meth)
    response = __data__.__send__(meth, *args, &block)

    if response.is_a?(__data__.class)
      with(__data__: response)
    else
      response
    end
  else
    super
  end
end
respond_to_missing?(meth, include_private = false) click to toggle source

@api private

Calls superclass method
# File lib/rom/changeset/stateful.rb, line 264
def respond_to_missing?(meth, include_private = false)
  super || __data__.respond_to?(meth)
end