[![Build status](https://secure.travis-ci.org/barsoom/attr_extras.png)](https://travis-ci.org/#!/barsoom/attr_extras/builds) # attr\_extras Takes some boilerplate out of Ruby, lowering the barrier to extracting small focused classes, without [the downsides of using `Struct`](http://thepugautomatic.com/2013/08/struct-inheritance-is-overused/). Instead of ``` class InvoiceBuilder def initialize(invoice, employee) @invoice, @employee = invoice, employee end private attr_reader :invoice, :employee end ``` you can just do ``` class InvoiceBuilder pattr_initialize :invoice, :employee end ``` This nicely complements Ruby's built-in `attr_accessor`, `attr_reader` and `attr_writer`. Supports positional arguments as well as optional and required hash arguments. Also provides conveniences for creating value objects, method objects, query methods and abstract methods. ## Usage ### `attr_initialize :foo, :bar` Defines an initializer that takes two arguments and assigns `@foo` and `@bar`. `attr_initialize :foo, [:bar, :baz!]` defines an initializer that takes one regular argument, assigning `@foo`, and one hash argument, assigning `@bar` (optional) and `@baz` (required). `attr_initialize [:bar, :baz!]` defines an initializer that takes one hash argument, assigning `@bar` (optional) and `@baz` (required). `attr_initialize` can also accept a block which will be invoked after initialization. This is useful for calling `super` appropriately in subclasses or initializing private data as necessary. ### `attr_private :foo, :bar` Defines private readers for `@foo` and `@bar`. ### `attr_value :foo, :bar` Defines public readers. Does not define writers, as [value objects](http://en.wikipedia.org/wiki/Value_object) are typically immutable. Defines object equality: two value objects of the same class with the same values are equal. ### `pattr_initialize :foo, :bar` Defines both initializer and private readers: shortcut for ``` attr_initialize :foo, :bar attr_private :foo, :bar ``` The `attr_initialize` notation for hash arguments is also supported: `pattr_initialize :foo, [:bar, :baz!]` ### `vattr_initialize :foo, :bar` Defines initializer, public readers and value object identity: shortcut for ``` attr_initialize :foo, :bar attr_value :foo, :bar ``` The `attr_initialize` notation for hash arguments is also supported: `vattr_initialize :foo, [:bar, :baz!]` ### `static_facade :fooable?, :foo`
Defines a `.fooable?` class method that delegates to an instance method by the same name, having first provided `foo` as a private reader. This is handy when a class-method API makes sense but you still want [the refactorability of instance methods](http://blog.codeclimate.com/blog/2012/11/14/why-ruby-class-methods-resist-refactoring/). ``` ruby class PublishingPolicy static_facade :allow?, :user static_facade :disallow?, :user def allow? user.admin? && … end def disallow? !allow? end end PublishingPolicy.allow?(user) ``` `static_facade :fooable?, :foo` is a shortcut for ``` ruby pattr_initialize :foo def self.fooable?(foo) new(foo).fooable? end ``` The `attr_initialize` notation for hash arguments is also supported: `static_facade :fooable?, :foo, [:bar, :baz!]` You don't have to specify arguments/readers if you don't want them: just `static_facade :fooable?` is also valid. ### `method_object :foo` *NOTE: v4.0.0 made a breaking change! `static_facade` does exactly what `method_object` used to do; the new `method_object` no longer accepts a method name argument.* Defines a `.call` class method that delegates to an instance method by the same name, having first provided `foo` as a private reader. This is a special case of `static_facade` for when you want a [Method Object](http://refactoring.com/catalog/replaceMethodWithMethodObject.html), and the class name itself will communicate the action it performs. ``` ruby class PriceCalculator method_object :order def call total * factor end private def total order.items.map(&:price).inject(:+) end def factor 1 + rand end end class Order def price PriceCalculator.call(self) end end ``` `method_object :foo` is a shortcut for ``` ruby static_facade :call, :foo ``` which is a shortcut for ``` ruby pattr_initialize :foo def self.call(foo) new(foo).call end ``` The `attr_initialize` notation for hash arguments is also supported: `method_object :foo, [:bar, :baz!]` You don't have to specify arguments/readers if you don't want them: just `method_object` is also valid. ### `attr_id_query :foo?, :bar?`
Defines query methods like `foo?`, which is true if (and only if) `foo_id` is truthy. Goes well with Active Record. ### `attr_query :foo?, :bar?`
Defines query methods like `foo?`, which is true if (and only if) `foo` is truthy. ### `attr_implement :foo, :bar`
Defines nullary (0-argument) methods `foo` and `bar` that raise e.g. `"Implement a 'foo()' method"`. `attr_implement :foo, [:name, :age]` will define a binary (2-argument) method `foo` that raises `"Implement a 'foo(name, age)' method"`. This is suitable for [abstract methods](http://en.wikipedia.org/wiki/Abstract_method#Abstract_methods) in base classes, e.g. when using the [template method pattern](http://en.wikipedia.org/wiki/Template_method_pattern). It's more or less a shortcut for ``` ruby def my_method raise "Implement me in a subclass!" end ``` though it is shorter, more declarative, gives you a clear message and handles edge cases you might not have thought about (see tests). ## Philosophy Findability is a core value. Hence the long name `attr_initialize`, so you see it when scanning for the initializer; and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method. ## Why not use `Struct`? See: ["Struct inheritance is overused"](http://thepugautomatic.com/2013/08/struct-inheritance-is-overused/) ## Why not use `private; attr_reader :foo`? Instead of `attr_private :foo`, you could do `private; attr_reader :foo`. Other than being more to type, declaring `attr_reader` after `private` will actually give you a warning (deserved or not) if you run Ruby with warnings turned on. If you don't want the dependency on `attr_extras`, you can get rid of the warnings with `attr_reader :foo; private :foo`. Or just define a regular private method. ## Installation Add this line to your application's `Gemfile`: gem "attr_extras" And then execute: bundle Or install it yourself as: gem install attr_extras ## Running the tests Run then with: `rake` Or to see warnings (try not to have any): `RUBYOPT=-w rake` ## Contributors * [Henrik Nyh](https://github.com/henrik) * [Joakim Kolsjö](https://github.com/joakimk) * [Victor Arias](https://github.com/victorarias) * [Teo Ljungberg](https://github.com/teoljungberg) * [Kim Persson](https://github.com/lavinia) * [Joe Ferris](https://github.com/jferris) ## License [MIT](LICENSE.txt)