computed_model 0.3.0 comes with a number of breaking changes. This guide will help you upgrade the library, but please test your program before deploying to production.
Before:
class User
include ComputedModel
endAfter:
class User
include ComputedModel::Model
endcomputed_model 0.3 checks if the requested field is a direct dependency.
If not, it raises ComputedModel::ForbiddenDependency.
This will mostly affect the following "indirect dependency" case:
Before:
class User
dependency :bar
computed def foo
baz # Accepted in computed_model 0.2
# ...
end
dependency :baz
computed def bar
# ...
end
endAfter:
class User
dependency :bar, :baz # Specify dependencies correctly
computed def foo
baz
# ...
end
dependency :baz
computed def bar
# ...
end
endBefore:
class User
dependency :bar
computed def foo
# ...
end
end
users = User.bulk_load_and_compute([:foo], ...)
users[0].bar # Accepted in computed_model 0.2After:
class User
dependency :bar
computed def foo
# ...
end
end
users = User.bulk_load_and_compute([:foo, :bar], ...) # Specify dependencies correctly
users[0].barPreviously, it sometimes happens to work depending on the order in which fields are loaded.
class User
# No dependency between foo and bar
dependency :raw_user
computed def foo
# ...
end
dependency :raw_user
computed def bar
foo
# ...
end
endIt was already fragile in computed_model 0.2.
However, in computed_model 0.3,
it always leads to ComputedModel::ForbiddenDependency.
Before:
class User
delegate_dependency :name, to: :raw_user, include_subdeps: true
endAfter:
class User
delegate_dependency :name, to: :raw_user, include_subfields: true
endWe also recommend renaming block parameters named subdeps as subfields,
although not strictly necessary.
It was useful in computed_model 0.1 but no longer needed in computed_model 0.2.
# No longer possible
self.computed_model_error = User::NotFound.newIt doesn't effect you if all dependency declarations are followed by computed def.
# Keeps working
dependency :foo
computed def bar
endOtherwise dependency might be consumed by the next define_loader or define_primary_loader.
Before:
dependency :foo # dependency of bar in computed_model 0.2
define_loader :quux, key: -> { id } do
# ...
end
computed def bar
# ...
endAfter:
# This would be interpreted as a dependency of quux
# dependency :foo
define_loader :quux, key: -> { id } do
# ...
end
dependency :foo # Place it here
computed def bar
# ...
endAdditionally, dependency before define_primary_loader will be an error.
Before:
class User
# Cyclic dependency is allowed as long as it's unused
dependency :bar
computed def foo
end
dependency :foo
computed def bar
end
end
users = User.bulk_load_and_compute([], ...) # Neither :foo nor :bar is usedAfter:
class User
# Remove cyclic dependency altogether
end
users = User.bulk_load_and_compute([], ...) # Neither :foo nor :bar is usedThey now have special meaning, so you should avoid using them as a normal subdependency.
They are constantly false condition in conditional dependency. Unless otherwise enabled, the dependency won't be used.
Before:
dependency foo: [nil, false] # foo will be used
computed def bar
endAfter:
# dependency foo: [nil, false] # foo won't be used
dependency foo: [:nil, :false] # Use other values instead
computed def bar
endThey are constantly true condition in conditional dependency. It's filtered out before passed to a loader or a primary loader.
Before:
dependency foo: [true] # true is given to "define_loader :foo do ... end"
computed def bar
endAfter:
# dependency foo: [true] # true is ignored
dependency foo: [:true] # Use other values instead
computed def bar
endCallable objects (objects implementing #call), including instances of Proc, is interpreted as a dynamic dependency.
Before:
dependency foo: -> { raise "foo" } # Passed to foo as-is
computed def bar
endAfter:
# dependency foo: -> { raise "foo" } # Dynamic dependency. Called during dependency resolution.
dependency foo: { callback: -> { raise "foo" } } # Wrap it in something
computed def bar
endIndependent fields may be loaded in an arbitrary order. But implementation-wise, this is to some degree predictable.
computed_model 0.3 uses different dependency resolution algorithm and may produce different orders. As a result, if your model is accidentally order-dependent, it may break with computed_model 0.3.
It won't affect you if you simply did include ComputedModel::Model (previously include ComputedModel) and nothing more.
Be cautious if you have a more complex inheritance/inclusion graph than that.
#verify_dependencies allows you to check the graph in the initialization process, rather than just before loading records.
We recommend doing this at the end of the class definition.
class User
define_loader ...
computed def ... end
verify_dependencies # HERE
end