FlexStruct
A drop-in replacement for Struct which adds a more flexible initialize method
Usage
Basic
Create a FlexStruct very similarly to how you'd use any other Struct:
Pet = FlexStruct.new(:species, :name, :colour)
However, FlexStruct-created classes can be initialized in more readable ways than
default Structs:
# Your only option with a Struct, positional arguments that require you to
# know which order the parameters were defined
floppy = Pet.new(:rabbit, "Floppy", :brown)
# Use hash/keyword arguments
fido = Pet.new(name: "Fido", species: :dog)
# Use block initialization
marmaduke = Pet.new do |pet|
pet.name = "Marmaduke"
pet.species = :cat
pet.colour = :tortoiseshell
end
Customisation
FlexStruct acts just like a Struct in other ways, you can customize its
behaviour and add functionality to the generated class by passing a block to the
FlexStruct constructor:
Person = FlexStruct.new(:forename, :surname) do
def fullname
[forename, surname].compact.join(" ")
end
end
Person.new(forename: "Finn", surname: "Mertens").fullname
# => "Finn Mertens"
It's possible to overwrite the initialize method this way - so be sure to use
super if you do this. Or better, don't do this, you probably don't need to.
Immutability
FlexStruct comes with a convenience method for making immutable Structs. This
will amend the generated struct to make it frozen after initialization:
Position = FlexStruct.new(:lat, :lng) { prepend FlexStruct::Frozen }
location = Position.new(lat: 44.681, lng: 169.101)
location.lat = -70.850
# => RuntimeError: can't modify frozen Position
Why?
Hashes are great. You can use them to store arbitrary key/value pairs using
any object as a key (although commonly people use Symbols).
However, Hashes are (by design) quite limited. Data is accessed using a small
subset of method calls, so if you're passing data around your Ruby program in a
Hash, every method using that data needs to know it's in a Hash(like) object.
OpenStructs are great. They behave a lot like a Hash that only has Symbol
keys - you can set arbitrary values against any named property. You also get to
get and set those values using regular Ruby methods, which means an OpenStruct
can be often used as a substitute for any other Ruby object, thanks to duck
typing. And to show how like-a-Hash it is, you can pass a Hash into its
initializer and have all of the corresponding properties set.
However, an OpenStruct will (by design) cheerfully respond nil to any
property it doesn't explicitly know about, and let you set values for any
property you like. While this is good in a lot of cases, you sometimes want
data structures that (like most Ruby objects) have a fixed API. OpenStruct
also has performance issues if you're going to create large numbers of them.
Structs are great. They're the most lightweight way to create a class with
fixed attributes - you can create a data structure without having to worry about
a lot of the nil issues that can crop up with the dynamic structures above.
However, once you've created a Struct class, the initialize method can only be
called using positional arguments - to initialize a Struct class with data, you
need to know the order its properties were initialized with. That's not
convenient or readable.
Yes, but why?
Structs are really useful classes that often make more sense than Hashes or
OpenStructs for a lot of their common use cases. But their inflexible
initializers make them a pain to use.
It's really easy to extend a Ruby Struct with new methods, including a better
initializer, but the amount of boilerplate required turns what would otherwise
be a one-liner class definition into a much heavier ugly addition.
FlexStruct just creates regular Struct-based classes with this smarter
initializer. The entire useful code of this gem is 14 lines, significantly
smaller than even this section of the README!
But to get people using data structures that are more useful, descriptive, and
lightweight, it should be as easy as possible to create them. Hence:
FlexStruct!
Installation
Add this line to your application's Gemfile:
gem 'flex_struct'And then execute:
$ bundle
Or install it yourself as:
$ gem install flex_struct
Development
After checking out the repo, run bin/setup to install dependencies. Then, run
rake spec to run the tests. You can also run bin/console for an interactive
prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To
release a new version, update the version number in version.rb, and then run
bundle exec rake release, which will create a git tag for the version, push
git commits and tags, and push the .gem file to
rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/bridgeu/flex_struct. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the FlexStruct project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.