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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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.


269
270
271
272
273
274
275
276
277
278
279
280
281
# 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

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


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

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:


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

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:


89
90
91
# 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) ⇒ Array<Pipe>, Dry::Transformer::Function

Define a changeset mapping excluded from diffs

Returns:

  • (Array<Pipe>, Dry::Transformer::Function)

    ]

See Also:


76
77
78
79
80
81
82
# 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) ⇒ 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.


94
95
96
97
98
99
# 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) ⇒ Array<Pipe>, Dry::Transformer::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>, Dry::Transformer::Function)

    ]

See Also:


60
61
62
63
64
65
66
# 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

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


102
103
104
# File 'lib/rom/changeset/stateful.rb', line 102

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

  • name (Symbol) (defaults to: Associated.infer_assoc_name(other))

    The association identifier from schema


225
226
227
# 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_optionsObject

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.


248
249
250
# File 'lib/rom/changeset/stateful.rb', line 248

def command_compiler_options
  super.merge(result: result)
end

#commitObject

Commit stateful changeset


203
204
205
# File 'lib/rom/changeset/stateful.rb', line 203

def commit
  command.call(self)
end

#data(data) ⇒ Changeset

Return changeset with data

Parameters:

  • data (Hash)

Returns:


170
171
172
# File 'lib/rom/changeset/stateful.rb', line 170

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:


151
152
153
154
155
156
157
158
159
160
161
# 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

#inspectString

Return string representation of the changeset

Returns:

  • (String)

243
244
245
# File 'lib/rom/changeset/stateful.rb', line 243

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:


134
135
136
# File 'lib/rom/changeset/stateful.rb', line 134

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)

234
235
236
# File 'lib/rom/changeset/stateful.rb', line 234

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)

193
194
195
# File 'lib/rom/changeset/stateful.rb', line 193

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)

181
182
183
# File 'lib/rom/changeset/stateful.rb', line 181

def to_h
  pipe.call(__data__)
end