https://github.com/umbrellio/verifly
This gem provides an api to run sequential checks
https://github.com/umbrellio/verifly
activemodel rails uber
Last synced: 11 months ago
JSON representation
This gem provides an api to run sequential checks
- Host: GitHub
- URL: https://github.com/umbrellio/verifly
- Owner: umbrellio
- License: mit
- Created: 2017-03-28T14:31:40.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2020-10-21T09:08:41.000Z (over 5 years ago)
- Last Synced: 2025-02-03T01:11:45.820Z (about 1 year ago)
- Topics: activemodel, rails, uber
- Language: Ruby
- Homepage:
- Size: 51.8 KB
- Stars: 1
- Watchers: 5
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Verifly v0.2
[](https://travis-ci.org/umbrellio/verifly)[](https://coveralls.io/github/umbrellio/verifly)
This gem provides an api to run sequential checks like
'ActiveModel::Validations' do, but with generic messages instead of errors.
It also provides additional components to build it which could be used
to imporve code readability. See [Applicator](#Applicator),
[ApplicatorWithOptions](#ApplicatorWithOptions) and
[ClassBuilder](#ClassBuilder) along with [Verifier](#Verifier)
## Instalation
```
$ gem install verifly
```
and then in code
```
require 'verifly'
```
## ClassBuilder
example:
```lang=ruby
Abstract = Struct.new(:data)
extend Verifly::ClassBuilder::Mixin
class WithString < self
def self.build_class(x)
self if x.is_a?(String)
end
end
Generic = Class.new(self)
self.buildable_classes = [WithString, Generic]
# or, vice versa
def self.buildable_classes
[WithString, Generic]
end
end
Abstract.build("foo") # => WithString.new("foo")
Abstract.build(:foo) # => Generic.new("foo")
```
or see it at rubydoc.info
Why don't just use Uber::Builder?
([Uber](https://github.com/apotonick/uber) is cool, you should try it)
There are two reasons: firstly, it is an unnecessary dependency.
We dont want npm hell, do we? Uber::Builder realy does not do much work,
it's just a pattern. Secondly, this implementation looks more clear to me,
because children are deciding whether they will handle arguments, not parents.
So to use it, you have to:
1. Write some classes with duck type `.class_builder(*args)`
2. Invoke `Verifly::ClassBuilder.new([<%= array_of_classes %>]).call(*args)`
3. ????
4. PROFIT
It's simple and clear, but not very sugary. So, otherwise, you may do
following:
1. Write an abstract class
2. Extend `Verifly::ClassBuilder::Mixin`
3. Inherit from the abstract class in different implementations
4. If some implementations have common ancestors
(not including the abstract class), you can implement common ancestor's
`.build_class` in terms of super (i.e.
`def self.build_class(x); super if x.is_a?(String); end`)
5. Change `.build_class` of other classes like `self if ...`.
Don't change default implementation's `.build_class`
6. Setup `.buildable_classes` on the abstract class, mentioning only direct
children if you done step 4
7. Optionally redefine `.build` in abstract class, if you want
to separate `build_class` and constructor params
8. Use `.build` instead of `new`
## Applicator
Applicator is designed to wrap applications of
[applicable](https://en.wikipedia.org/wiki/Sepulka) objects
around some binding in some context
example:
```lang=ruby
object = OpenStruct.new(foo: :bar)
Applicator.call(:foo, object, {}) # => :bar
Applicator.call('foo', object, {}) # => :bar
Applicator.call('context', object, {}) # => {}
Applicator.call(-> { foo }, object, {}) # => :bar
Applicator.call(->(context) { context[foo] }, object, bar: :baz) # => :baz
Applicator.call(true, object, {}) # => true
foo = :bar
Applicator.call(:foo, binding, {}) # => :bar
Applicator.call('object.foo', binding, {}) # => :bar
```
Applicator is good, but in most cases
[ApplicatorWithOptions](#ApplicatorWithOptions) would be a better option.
## ApplicatorWithOptions
ApplicatorWithOptions is an applicator with options.
The options are `if: ` and `unless: `. Same as in ActiveModel::Validations,
they are applied to the same binding. Main action is executed
only if `if: ` evaluates to truthy and `unless: ` evaluates to falsey.
See examples:
```lang=ruby
ApplicatorWithOptions.new(:foo, if: -> { true }).call(binding, {}) # => foo
ApplicatorWithOptions.new(:foo, if: -> (context) { context[:bar] })
.call(binding, { bar: true }) # => foo
ApplicatorWithOptions.new(:foo, if: { bar: true }).call(binding, :bar) # => foo
ApplicatorWithOptions.new(:foo, unless: -> { true })
.call(binding, {}) # => nil
ApplicatorWithOptions.new(:foo, unless: -> (context) { context[:bar] })
.call(binding, { bar: true }) # => foo
ApplicatorWithOptions.new(:foo, unless: { bar: true })
.call(binding, :bar) # => nil
```
## Verifier
The last, but the most interesting component is Verifier.
Verifiers use ApplciatorWithOptions to execute generic procedures.
Procedures should call `message!` if they want to yield something.
Note that you should implement `message!` by yourself (in terms of super)
```lang=ruby
class MyVerifier < Verifly::Verifier
Message = Struct.new(:text)
verify :foo, if: { foo: true }
private
def message!(text)
super { Message.new(text) }
end
def foo
message!('Something is wrong') if Fixnum != Bignum
end
end
```
In addition to Applicator's power, you also can nest your verifiers
to split the logic
```lang=ruby
class MyVerifier < Verifly::Verifier
Message = Struct.new(:text)
verify_with ChildVerifier, if: -> (context) { cotnext[:foo] }
private
def message!(text)
super { Message.new(text) }
end
end
class ChildVerifier < MyVerifier
verify %q(message!("it's alive!"))
end
```
## Yard documentation
This gem uses yard to generate documentation about its API.
Visit http://www.rubydoc.info/github/umbrellio/verifly/master to see
actual documentation for master.