I’m working on a Ruby binding for a REST API and am struggling with what would be considered idiomatic Ruby for a piece of it.
To illustrate with a simplified example, the resources represented by the API form a tree structure:
root
/\
/ \
/ \
group group
/ /\
/ / \
/ / \
project project project
To create a new instance of a child, knowledge of its parent is required. For example, to create a new group it must have knowledge of the root so that it can retrieve the URI for creating a group from the root.
I’m reasonably happy with read access:
api = Api.new(...)
api.groups # An array of all groups
api.groups.select(...).projects # An array of all a group's projects
It’s creating new resources that I’m unsure about.
I’ve considered having a create method on an array subclass:
group = api.groups.create("group-name")
project = group.projects.create("project-name")
Instantiating the type directly, taking the parent as a constructor argument:
group = Group.new(api, "project-name")
project = Project.new(group, "project-name")
Create methods on the parent:
group = api.create_group("group-name")
project = group.create_project("project-name")
Of the three approaches above, I prefer the first as it’s concise and allows creation to be chained. The third option is similar, but it doesn’t feel right – in terms of the REST API, the create is happening at the wrong level as it’s the groups resource the allows creation of a group. That said, I’m not an experienced Ruby developer and, first and foremost, I want the Ruby API to be idiomatic for Ruby developers even if that means it doesn’t exactly match the model of the REST API.
It’s not clear to me how the code samples and description provided ties in to REST, but I’ll offer my two cents on what I do understand:
I think it would be perfectly valid to use a combination of all three. As for what would be idiomatic, I’d take your cues from the Rails association methods.
api.groups.createfollows Rails’has_manysyntax.api.create_groupandgroup.create_projectfollow thehas_onesyntax. And I’m assuming that, beneath the syntactic sugar ofcreate/create_association, you are using composition to tie together the references to all the classes anyway, soGroup.new(api, "group-name")/Project.new(group, "project-name")seem like perfectly valid options as well. There’s no rule saying you can’t use all three approaches to good effect (and Rails does).I’d suggest avoiding subclassing Ruby’s base classes (you mentioned subclassing Array) unless you have a really good reason to do so. Just use composition to delegate to them when necessary.