Eigindir
Coercible PORO attributes.
Synopsis
This is a tiny virtus-inspired library for PORO attributes' definition and coersion.
Like virtus it declares attributes, and coerces their values. Unlike virtus it uses different coersion mechanism, and does nothing else.
class Foo
include Eigindir
attribute(
:bar,
writer: ->(v) { v.to_i + 1 },
reader: ->(v) { v.to_s }
)
end
foo = Foo.new
# the setter coerced given value:
foo. = "10"
foo.instance_eval "@bar" # => 11
# the getter coerced stored value:
foo.instance_eval # => "11"
# attributes getter and setter are defined as well:
foo.attributes = { "bar" => 5 }
foo.attributes # => { bar: "6" }
The module doesn't define the initializer. You're free to define in a following way:
class Foo
include Eigindir
def initializer(**)
self.attributes =
end
end
Details
Base Use
attribute
, attribute_reader
and attribute_writer
are like
attr_accessor
, attr_reader
and attr_writer
:
class Foo
include Eigindir
attribute :bar
attribute_reader :baz
attribute_writer :qux
end
foo = Foo.new
foo. = 1
foo. # => 1
foo.baz = 1 # <NoMethodError>
foo.baz # => nil
foo.qux = 1
foo.instance_eval "@qux" # => 1
foo.qux # <NoMethodError>
Attributes Declaration
The attributes
and attributes=
ignore all keys not declared as attribute
.
class Foo
include Eigindir
attribute :bar
attr_accessor :baz
end
foo = Foo.new
foo.attributes = { bar: 1, baz: 2 }
foo. # => 1
foo.baz # => nil
foo.baz = 2
foo.baz # => 2
foo.attributes # => { bar: 1 }
The methods ignore attributes that has no getters or setters correspondingly:
class Foo
include Eigindir
attribute_reader :bar
attribute_writer :baz
end
foo = Foo.new
foo.attributes = { bar: 1, baz: 1 }
# baz has no getter,
# bar value is not assigned because it has no setter:
foo.attributes = { bar: nil }
The attributes=
accepts hash with any stringified keys and symbolizes them:
class Foo
include Eigindir
attribute :bar
attribute :baz
end
foo = Foo.new
foo.attributes = { bar: "1", "bar" => 2 }
# The setter symbolizes keys to :bar and uses last value:
foo.attributes # => { bar: 2 }
Base Coersion
All class methods: attribute
, attribute_reader
and attribute_writer
accepts the :coerce
key:
class Foo
include Eigindir
attribute :bar, coerce: ->(val) { val.to_i + 1 }
end
The setter coerces given value:
foo = Foo.new
foo. = "1"
foo.instance_eval "@bar" # => 2
The getter coerces stored variable:
foo.instance_eval "@bar" # => 2
foo. # => 3
A coercer can be set either as proc or lambda, or by instance method name. The following declarations are equivalent:
class Foo
include Eigindir
attribute :bar, coerce: ->(value) { value.to_s }
attribute :bar, coerce: lambda { |value| value.to_s }
attribute :bar, coerce: proc { |value| value.to_s }
attribute :bar, coerce: :coercer
attribute :bar, coerce: "coercer"
private
def coercer(value)
value.to_s
end
end
Separate Coersion
The attribute
class method also takes :reader
and :writer
.
Every option sets its own coersion for the corresponding direction:
class Foo
include Eigindir
attribute(
:bar,
writer: ->(val) { val.to_i + 1 }
reader: ->(val) { val.to_s }
)
end
foo = Foo.new
# The setter uses the :writer coercer:
foo.bar = "10"
foo.instance_eval "@bar" # => 11
# The getter uses the :reader coercer:
foo.bar # => "11"
Coersion of nil
Because nil
stands for nothing, it is not coerced by default:
class Foo
include Eigindir
attribute :bar, coerce: ->(value) { value.to_s }
end
foo = Foo.new
foo. = nil
foo.instance_eval "@bar" # => nil
foo. # => nil
Use strict: true
option to coerce nil
as well:
class Foo
include Eigindir
attribute :baz, coerce: ->(value) { value.to_s }, strict: true
end
foo = Foo.new
foo. = nil
foo.instance_eval "@bar" # => ""
foo. # => ""
This can be used to set default values explicitly either by getter or setter:
class Foo
include Eigindir
attribute :bar, reader: proc { |val| val || 1 }, strict: true
attribute :baz, writer: proc { |val| val || 1 }, strict: true
end
foo = Foo.new
# The getter's default is pretty virtual:
foo. = nil
foo.instance_eval "@bar" # => nil
foo. # => 1
# While the setter's is persistent:
foo.baz = nil
foo.instance_eval "@baz" # => 1
foo.baz # => 1
Credits
Many thanks to Icelandic for the name of the module.
Installation
Add this line to your application's Gemfile:
# Gemfile
gem "eigindir"
Then execute:
bundle
Or add it manually:
gem install eigindir
Compatibility
Tested under MRI rubies 2.1+ only.
It uses Ruby 2.1+ refinements that aren't yet supported by Rubinius and JRuby.
Uses RSpec 3.0+ for testing and hexx-suit for dev/test tools collection.
Contributing
- Fork the project.
- Read the STYLEGUIDE.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with Rakefile or version (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
- Send me a pull request. Bonus points for topic branches.
License
See the MIT LICENSE.