Closed tdsmith closed 8 years ago
Hey,
Thanks for the suggestion! I'm going to have to think a bit on this and discuss it with the team – it's not a pattern we use ourselves. Could you give an example of how you would use it?
Yes, sorry! This is useful so you can implement method chaining to get a so-called "fluent interface," which looks something like this in use:
pizza = PizzaBuilder.new(12).
crust(:whole_wheat).
withTopping(:spinach).
build
instead of
builder = PizzaBuilder.new(12)
builder.crust(:whole_wheat)
builder.withTopping(:spinach)
pizza = builder.build
The primary advantage is to avoid the need to name a temporary (e.g.) PizzaBuilder object and it's arguably easier to read.
@tdsmith Oh, sorry, I was unclear – I know about the kind of interface, I was more curious what you use it for specifically :) Since it's something I've seen as a design choice in certain libs, but it's not a pattern our team uses day to day.
The team is currently leaning towards not wanting to include this, since it doesn't seem core to what we see attr_extras as: reducing some boilerplate around extracting small objects (though we do have some stuff in currently that is outside that – we've been thinking about (re)moving those things).
This seems less general purpose and more like something we'd use in very specific situations if at all – I think in the example above, we'd probably go with something like PizzaBuilder.call(id: 12, crust: :whole_wheat, …)
using attr_extra's method_object
.
A fluent interface makes a lot of sense for stuff like Active Record queries and scopes, but that's a rather specific case, where it would probably be fine to have to do it manually or write your own abstraction inside that library.
But all this is based on our usage patterns – would love to learn about others :)
Oops, sorry, my mistake. I don't think I'm sophisticated or prolific enough to give you a useful answer but I was fleetingly inspired by the Docile documentation which I think is intending to give advice on how to build a class that can be both a) used with instance_eval (via Docile's dsl_eval) to define a DSL (which just requires writers, but is where the appeal of the builder pattern arises) and b) nicely instantiated "by hand" (which afaict is where the fluent aspect comes in).
I don't think I see what the method_object
approach adds over calling a Pizza constructor (or what the parameter to method_object should be), which makes me worry I'm misanswering you again.
Ah, no worries :)
We tend use method_object
for things like builders – when a class effectively acts as a single public method/function. It's basically a shortcut for PizzaBuilder.new(…).build
with the benefits of instance-level code (easier to refactor and such), but with a shallower (and thus less fragile, and easier to stub in unit tests) API like PizzaBuilder.call(…)
. And it communicates that we're dealing with a "method object". Doced here: https://github.com/barsoom/attr_extras#method_object
So currently we feel that this isn't functionality that fits into our vision for attr_extras, but there seem to be other libraries for that. And if someone wants an API more like ours, maybe someone could make an attr_fluent_writer
gem?
Closing this ticket – hope that's OK!
I'm implementing a builder pattern that has lots of code like:
A
attr_fluent_writer
or something that covers this case would be handy to have here.