Repository::Support
Contents
- Overview
- IMPORTANT LEGACY NOTICE
- Installation
- Usage
-
StoreResultStoreResult::SuccessStoreResult::Failure
ErrorFactoryTestAttributeContainer- A Note on Parameters
-
- Contributing
- Version History
- Legal
Overview
This Gem provides several support classes and modules for
Repository::Base and its
user-created subclasses, which implement the Repository layer of a typical Data
Mapper pattern architecture.
These classes and modules are:
-
ErrorFactoryprovides a single class method,.createwhich, when supplied with anActiveModel::Errors-quacking object as a parameter, produces an Array of Hashes containing JSON-compatible error information; -
ResultBuilderis a Command-pattern class whose#initializemethod takes one parameter and whose#buildmethod evaluates that value. If it is truthy, then#buildreturns aStoreResult::Success(see below) with that value as its "paylaaod". If the value is falsy, then#buildreturns aStoreResult#Failure, yielding the value to a block that returns the payload for theStoreResult. -
StoreResultis a simple value object with three accessors for values passed in to the#initializemethod: namely#entity(some value object);#success(a Boolean, aliased as#success?); and#errorsan Array of error records as created byErrorFactory.create. It has two subclasses:StoreResult::Successfills in aStoreResultusing its single parameter (the entity) and defaults for the other fields; andStoreResult::Failure, which does likewise initialised with an array of error hashes. -
TestAttributeContaineris a module that, when used to extend a class, adds anattributesHash property (reader and writer) to the extending class. Whileattributesis initially empty, it may be added to either by defining a single key, or by mass-assigning a Hash toattributes. Once an individual "attribute" is defined for a class instance, it can be read from or written to using a direct method on that instance. See the discussion in "Usage" below for more details.
IMPORTANT LEGACY NOTICE
NOTICE! This Gem was created to support a solo, ad-hoc, early learning experience in what is now known as Clean Architecture. It was part of our first attempt to build an alternative to the ActiveRecord/ActiveModel scheme native to Ruby on Rails.
As such, it has been superseded and far outshone by other, team efforts, notably ROM as used with Hanami and Trailblazer. You are strongly advised to examine these and other tools rather than to use this for any new development. The Gem is being republished as an 0.1.0 release purely for internal archaeologigical purposes.
Installation
Add this line to your application's Gemfile:
gem 'repository-support'And then execute:
$ bundle
Or install it yourself as:
$ gem install repository-support
Usage
StoreResult
StoreReult is used as the return value from all Repository::Base instance
methods (actions) except #all.
If the action implemented by the method was successful, it returns a
StoreResult where
- the
entityattribute is an entity matching the state of the record persisted or accessed by the action; - the
successattribute (or#success?method) istrue; and - the
errorsattribute is an empty Array.
If the action was unsuccessful, the repository method returns a StoreResult
where
- the
entityattribute isnil; - the
successattribute (or#success?method) isfalse; and - the
errorsattribute contains one error Hash for each error preventing the action from succeeding.
StoreResult::Success
This subclass of StoreResult is a convenience for initialising a successful
StoreResult. Its #initialize method takes a single parameter, the entity to
be used in the result, with the other fields set as described above for a
successful result.
StoreResult::Failure
This subclass of StoreResult is a convenience for initialising an unsuccessful
StoreResult. Its #initialize method takes a single parameter, the Array of
error hashes to be used in the result, with the other fields set as described
above for an unsuccessful result.
ErrorFactory
This class has a single class method, .create. Given a parameter value that
quacks as anActiveModel::Errors
instance, it returns an Array where each item is a Hash derived from each error
reported by the parameter object, or an empty Array if there are no errors. Each
Hash in the Array has two fields:
-
field, which contains the attribute passed toActiveModel::Errors#addas a string; and -
message, which contains the message as passed into the same#addcall.
So, given an ActiveModel::Errors object that resulted from the following code:
errors = ActiveModel::Errors.new self
# ...
errors.add :frobulator, 'does not frob'
errors.add :frobulator, `is busted'
errors.add :foo, 'is :foo'
# ...
error_data = ErrorFactory.create errors
@logger.log error_datathe value of error_data written to the log would be (formatted for clarity)
[
{field: 'frobulator', message: 'does not frob'},
{field: 'frobulator', message: 'is busted'},
{field: 'foo', 'is :foo'}
]
Note that no guarantees are made for ordering, just as seems to be the case for
ActiveModel::Errors.
TestAttributeContainer
This module implements support for attributes in a way that can be thought of as "halfway between a Struct and an OpenStruct or FancyOpenStruct."
By extending a class with the module and invoking the init_empty_attribute_container class method within that class, a Hash is added as the attributes attribute of each instance of that class. It can be assigned to directly; once having done so, individual "attributes" may be accessed or modified through a method call using the name of the attribute.
For example:
class Foo
extend Repository::Support::TestAttributeContainer
init_empty_attribute_container
end
# interactively
foo = Foo.new
# => #<Foo:0x007fd2b4b9da28>
foo.attributes
# => {}
foo.attributes = { foo: true, bar: 42 }
# => {:foo=>true, :bar=>42}
foo.foo
# => true
foo.foo = :whatever_you_want
# => :whatever_you_want
foo.attributes
# => {:foo=>:whatever_you_want, :bar=>42}
foo.quux
# => NoMethodError: undefined method `quux' # ...
foo.attributes[:quux] = 'hello'
# => "hello"
foo.quux
# => "hello"To create a new attribute after the container has been set up, assign to a new key in the attributes property Hash. As demonstrated above, the "attribute" can then be accessed or modified by using its name as a reader or writer method name. Without explicitly assigning to attributes, however, undefined methods raise errors as usual.
A Note on Parameters
All public methods having multiple arguments (including #initialize) in each
of the classes defined above use the keyword-argument specification introduced
in Ruby 2.0. By removing order dependency of arguments, inadvertent-reordering
errors are no longer a
hunt-the-typo
exercise. This rule does not apply to single-parameter methods, nor to
private methods.
Contributing
- Fork it ( https://github.com/jdickey/repository-support/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Ensure that your changes are completely covered by passing specs, and comply with the Ruby Style Guide as enforced by RuboCop. To verify this, run
bundle exec rake, noting and repairing any lapses in coverage or style violations; - Commit your changes (
git commit -a). Please do not use a single-line commit message (git commit -am "some message"). A good commit message notes what was changed and why in sufficient detail that a relative newcomer to the code can understand your reasoning and your code; - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request. Describe at some length the rationale for your new feature; your implementation strategy at a higher level than each individual commit message; anything future maintainers should be aware of; and so on. If this is a modification to existing code, reference the open issue being addressed.
- Don't be discouraged if the PR generates a discussion that leads to further refinement of your PR through additional commits. These should generally be discussed in comments on the PR itself; discussion in the Gitter room (see below) may also be useful;
- If you've comments, questions, or just want to talk through your ideas, don't hesitate to hang out in the
Repository::Baseroom on Gitter. Ask away!
Version History
| Version | Date | Notes |
|---|---|---|
| v0.1.0 | 2 February 2018 | Changed MRI supported version from 2.2.2 to 2.5.0; published legacy notice |
| v0.0.4 | 9 March 2015 | Added experimental, one-off JRuby 9000 support |
| v0.0.3 | 21 February 2015 | Completed initial feature development |
| v0.0.2 | 18 February 2015 | Internal; incremental feature development |
| v0.0.1 | 18 February 2015 | Internal; incremental feature development |
Legal
This document and the accompanying code are Copyright © 2015-2018 by Jeff Dickey/Seven Sigma Agility, and are released under the terms of the MIT License.