Skip to content

Latest commit

 

History

History
184 lines (124 loc) · 7.22 KB

CHANGELOG.MD

File metadata and controls

184 lines (124 loc) · 7.22 KB

Changelog

Subroutine 4.2.0

If you are using polymorphic association fields, you can now customize how Subroutine resolves those class names to a ruby class by setting a global callable/lambda/proc:

::Subroutine.constantize_polymorphic_class_name = ->(class_name) do
  class_name.classify.constantize
end

Subroutine 4.1.4

Fields using the time/timestamp/datetime caster will now default back to the old behavior, and use a precision: option to opt-in to the new behavior introduced in v4.1.1.

precision: :seconds will retain the old behavior of always parsing to a new Time object with floored sub-second precision, but applied more forcefully than before as it would have parsed whatever you passed to it. (This is the default, now.)

precision: :high will now use the new functionality of re-using Time objects when they are passed in, or if not parsing exactly the provided string as to a Time object.

Subroutine 4.1.1

Fields using the time/timestamp/datetime caster will now return exactly the passed in value if it acts like a time object (acts_like?(:time)/acts_like_time?), instead of serializing to string and re-parsing to a Time object. This fixes issues with losing usec precision.

Subroutine 4.1.0

A field can now opt out of the natural assignment behavior of ActiveSupport::HashWithIndifferentAccess. Top level param groups are still accessible via indifferent access but if a field sets the bypass_indifferent_assignment option to true the HashWithIndifferentAccess assignment behavior will be bypassed in favor of direct Hash-like assignment.

class MyOp < Subroutine::Op

  object :some_hash
  object :some_other_hash, bypass_indifferent_assignment: true

end

Subroutine 4.0.1

Association fields can now use find_by() instead of find_by!() by passing a raise_on_miss: false option. This places the responsibility on the op to manage nil cases rather than handling RecordNotFound errors.

Subroutine 4.0

The Subroutine::Fields module now contains a class_attribute that allows the altering of param accessor behaviors. include_defaults_in_params is now available to opt into including the default values in usage of the all_params (alias params) method. Backwards compatibility is preserved by defaulting the value to false. If switched to true, when an input is omitted and the field is configured with a default value, it will be included in the all_params object.

Removed all usage of ungrouped params and refactored the storage of grouped params. Params are now stored in either the provided group or the defaults group and accessed via provided_params, params, default_params, and params_with_defaults. Grouped params are accessed the same way but with the group name prefixed eg. my_private_default_params.

Polymorphic association fields now resolve class names via klass.camelize.constantize, previously was klass.classify.constantize.

Subroutine 3.0

Add support for Rails 6.1. Drop support for Rails 6.0 and lower.

Subroutine 2.3

Support dynamic types for foreign keys on association fields. The class type is used at runtime to determine the casting behavior of the foreign key field.

Subroutine 2.2

Add type validation for Output.

Subroutine 2.0

The updates between 1.0 and 2.0 are relatively minor and are focused more on cleaning up the codebase and extending the use of the 0.9->1.0 refactor. There are, however, breaking changes to how associations are loaded. The association is no longer loaded via find() but rather find_by!(id:). Given this, a major version was released.

Note: 2.0.0 was released with a bug and subsequently yanked. 2.0.1 is the first available 2.x version.

Subroutine 1.0

A massive refactor took place between 0.9 and 1.0, including breaking changes. The goal was to reduce complexity, simplify backtraces, and increase the overall safety and reliability of the library.

Subroutine::Fields

Subroutine::Fields was completely refactored to manage field declaration, configuration, and access in a more systematic and safer way.

Op._fields was removed in favor of Op.field_configurations. field_configurations is a hash with keys of the field name and values of FieldConfiguration objects. FieldConfiguration objects are SimpleDelegates to the underlying option hashes. They answer questions and provide a mechanism for validating the configuration of a field.

Fields can be accessed via helpers and accessors can be managed by the field declration. Helpers include get_field, set_field, and clear_field.

class SomeOp < ::Subroutine::Op
  string :foo, read_accessor: field_reader: false, field_writer: true

  def perform
    self.foo = "bar"
    self.foo # NoMethodError
    self.get_field(:foo) # => "bar"
  end
end

Fields can be omitted from mass assignment, meaning they would not be respected via constructor signatures.

class SomeOp < ::Subroutine::Op
  string :foo, mass_assignable: false
  def perform
    puts foo
  end
end

SomeOp.submit!(foo: "Hello World!") # raises ::Subroutine::Fields::MassAssignmentError
SomeOp.new{|op| op.foo = "Hello World!" }.submit! # prints "Hello World!"

This is especially useful when dealing with user input and potentially unsafe attributes.

class UserUpdateOp < ::Op
  association :user
  string :first_name
  string :last_name
  integer :credit_balance_cents, mass_assignable: false

  def perform
    user.update(params)
  end
end

# some_controller.rb
def update
  UserUpdateOp.submit!(params.merge(user: current_user))
end

Field groups were added as well, allowing you to access subsets of the fields easily.

class AccountUpdateOp < ::Op
  association :account

  with_options group: :user do
    string :first_name
    string :last_name
    date :dob
  end

  with_options group: :business do
    string :company_name
    string :ein
  end

  def perform
    account.user.update(user_params)
    account.business.update(business_params)
  end

end

ActionController::Parameters from Rails 5+ are now transformed to a hash in Subroutine::Fields by default. This means strong parameters are essentially unused when passing Subroutine::Fields.

Read more about field management and access in https://github.com/guideline-tech/subroutine/wiki/Param-Usage

Subroutine::Association

The Subroutine::Association module has been moved to Subroutine::AssociationFields.

Only native types are stored in params now. The objects loaded from associations are stored in an association_cache. This ensures access to fields are consistent regardless of the inputs.

class SomeOp < ::Subroutine::Op
  association :user
  association :resource, polymorphic: true
end

user = User.find(4)

op = SomeOp.new(user: user, resource: user)
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
op.params_with_association #=> { user: <User:103204 @id=4>, resource: <User:103204 @id=4> }

op = SomeOp.new(user_id: user.id, resource_type: "User", resource_id: user.id)
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
op.params_with_association #=> { user: <User:290053 @id=4>, resource: <User:29042 @id=4> }

Assignment of associations now validates the type. If an association is not polymorphic, the type will be validated against the expected type.