FlexStruct

A drop-in replacement for Struct which adds a more flexible initialize method

Usage

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

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/gareth/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.