Class: ROM::Changeset::Stateful Abstract

Inherits:
ROM::Changeset show all
Defined in:
lib/rom/changeset/stateful.rb

Overview

This class is abstract.

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

Direct Known Subclasses

Create, Update

Constant Summary collapse

EMPTY_PIPE =

Default no-op pipe

Pipe.new(use_for_diff: false).freeze

Constants inherited from ROM::Changeset

DEFAULT_COMMAND_OPTS, VERSION

Instance Attribute Summary collapse

Attributes inherited from ROM::Changeset

#command_type, #relation

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ROM::Changeset

[], command_type, #new, relation

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object (private)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/rom/changeset/stateful.rb', line 266

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

Instance Attribute Details

#__data__Hash (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The relation data.

Returns:

  • (Hash)

    The relation data



16
# File 'lib/rom/changeset/stateful.rb', line 16

option :__data__, optional: true

#pipeChangeset::Pipe (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Data transformation pipe

Returns:



21
# File 'lib/rom/changeset/stateful.rb', line 21

option :pipe, reader: false, optional: true

Class Method Details

.default_pipe(context) ⇒ Pipe

Build default pipe object

This can be overridden in a custom changeset subclass

Returns:



87
88
89
# File 'lib/rom/changeset/stateful.rb', line 87

def self.default_pipe(context)
  pipes.size > 0 ? pipes.map { |p| p.bind(context) }.reduce(:>>) : EMPTY_PIPE
end

.extend(&block) ⇒ Array<Pipe>, Transproc::Function

Define a changeset mapping excluded from diffs

Returns:

  • (Array<Pipe>, Transproc::Function)

    ]

See Also:



74
75
76
77
78
79
80
# File 'lib/rom/changeset/stateful.rb', line 74

def self.extend(*, &block)
  if block
    map(use_for_diff: false, &block)
  else
    super
  end
end

.inherited(klass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



92
93
94
95
96
# File 'lib/rom/changeset/stateful.rb', line 92

def self.inherited(klass)
  return if klass == ROM::Changeset
  super
  klass.instance_variable_set(:@__pipes__, pipes.dup)
end

.map(options = EMPTY_HASH, &block) ⇒ Array<Pipe>, Transproc::Function

Define a changeset mapping

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

Examples:

Transformation DSL

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

Using custom block

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

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

Returns:

  • (Array<Pipe>, Transproc::Function)

    ]

See Also:



58
59
60
61
62
63
64
# File 'lib/rom/changeset/stateful.rb', line 58

def self.map(options = EMPTY_HASH, &block)
  if block.parameters.empty?
    pipes << Class.new(Pipe, &block).new(options)
  else
    pipes << Pipe.new(block, options)
  end
end

.pipesObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



99
100
101
# File 'lib/rom/changeset/stateful.rb', line 99

def self.pipes
  @__pipes__
end

Instance Method Details

#associate(other, name = Associated.infer_assoc_name(other)) ⇒ Object

Associate a changeset with another changeset or hash-like object

Examples:

with another changeset

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

new_task.associate(new_user, :users)

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)

Parameters:

  • other (#to_hash, Changeset)

    Other changeset or hash-like object

  • assoc (Symbol)

    The association identifier from schema



222
223
224
# File 'lib/rom/changeset/stateful.rb', line 222

def associate(other, name = Associated.infer_assoc_name(other))
  Associated.new(self, associations: { name => other })
end

#commandObject



236
237
238
# File 'lib/rom/changeset/stateful.rb', line 236

def command
  relation.command(command_type, DEFAULT_COMMAND_OPTS.merge(result: result))
end

#commitObject

Commit stateful changeset



200
201
202
# File 'lib/rom/changeset/stateful.rb', line 200

def commit
  command.call(self)
end

#data(data) ⇒ Changeset

Return changeset with data

Parameters:

  • data (Hash)

Returns:



167
168
169
# File 'lib/rom/changeset/stateful.rb', line 167

def data(data)
  with(__data__: data)
end

#extend(*steps, **options, &block) ⇒ Changeset

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.

Returns:

See Also:



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/rom/changeset/stateful.rb', line 148

def extend(*steps, **options, &block)
  if block
    if steps.size > 0
      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

#inspectString

Return string representation of the changeset

Returns:

  • (String)


245
246
247
# File 'lib/rom/changeset/stateful.rb', line 245

def inspect
  %(#<#{self.class} relation=#{relation.name.inspect} data=#{__data__}>)
end

#map(*steps) ⇒ Changeset #map(&block) ⇒ Changeset #map(*steps, &block) ⇒ Changeset

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

Overloads:

  • #map(*steps) ⇒ Changeset

    Apply mapping using built-in transformations

    Examples:

    changeset.map(:add_timestamps)
    

    Parameters:

    • steps (Array<Symbol>)

      A list of mapping steps

  • #map(&block) ⇒ Changeset

    Apply mapping using a custom block

    Examples:

    changeset.map { |tuple| tuple.merge(created_at: Time.now) }
    
  • #map(*steps, &block) ⇒ Changeset

    Apply mapping using built-in transformations and a custom block

    Examples:

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

    Parameters:

    • steps (Array<Symbol>)

      A list of mapping steps

Returns:



131
132
133
# File 'lib/rom/changeset/stateful.rb', line 131

def map(*steps, &block)
  extend(*steps, for_diff: true, &block)
end

#resultSymbol

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return command result type

Returns:

  • (Symbol)


231
232
233
# File 'lib/rom/changeset/stateful.rb', line 231

def result
  __data__.is_a?(Array) ? :many : :one
end

#to_aArray Also known as: to_ary

Coerce changeset to an array

This will send the data through the pipe

Returns:

  • (Array)


190
191
192
# File 'lib/rom/changeset/stateful.rb', line 190

def to_a
  result == :one ? [to_h] : __data__.map { |element| pipe.call(element) }
end

#to_hHash Also known as: to_hash

Coerce changeset to a hash

This will send the data through the pipe

Returns:

  • (Hash)


178
179
180
# File 'lib/rom/changeset/stateful.rb', line 178

def to_h
  pipe.call(__data__)
end