# RiceBubble Simple serialization with a sprinkling of type safety. Part of your complete breakfast. ## Installation Install the gem and add to the application's Gemfile by executing: $ bundle add rice_bubble If bundler is not being used to manage dependencies, install the gem by executing: $ gem install rice_bubble ## Usage Create a subclass of `RiceBubble::Serializer` and add some attributes: ```ruby class CharacterSerializer < RiceBubble::Serializer attributes( name: string, player_name: optional(string), class: enum('fighter', 'wizard', 'thief'), stats: object( strength: integer, dexterity: integer, constitution: integer, intelligence: integer, wisdom: integer, charisma: integer, ) ) end ``` Then you can use the new serializer to convert your data to a JSON-friendly representation: ```ruby json = CharacterSerializer.new(my_character).call # => { name: "...", ... } ``` **Note:** you can also use `CharacterSerializer.call(my_character)`. ### Attribute types | Name | Expects | Options | Example | | ------------ | -------------------------- | ---------------------------------------- | ------------------------------------------------ | | `any` | any of the specified types | array of attribute types | `any(string, integer)` | | `boolean` | `true` or `false` | | `boolean` | | `date` | `Date` or `#to_date` | | `date` | | `enum` | matching string | array of string values | `enum('red', 'blue', 'green')` | | `integer` | `Integer` or `#to_i` | `max`, `min` | `integer(min: 3, max: 20)` | | `literal` | exactly matching value | literal | `literal('foo')` | | `number` | `Numeric` | `max`, `min` | `number(min: 3, max: 20)` | | `object` | `Object` or `Hash` | hash of object names and attribute types | `object(name: string, age: integer)` | | `optional` | value or `nil` | attribute type | `optional(string)` | | `serialized` | `Object` or `Hash` | `Serializer` class | `serialized(SkillSerializer)` | | `string` | `String` or `#to_s` | `max`, `min`, `format` | `string(min: 3, max: 20, format: /\A[a-z]+\z/i)` | | `time` | `Time` or `#to_time` | | `time` | In this way, you can recursively build up quite complex nested structures of attributes in your serializer. ### `optional` All attributes are required unless marked as optional. You can either do this with the `optional` attribute type, or by appending `.optional` to the attribute definition. That is, the following are equivalent: ```ruby optional(string(min: 3)) string(min: 3).optional ``` ### Implementing your own attribute types The attribute types are looked up automatically in the namespace `RiceBubble::Attributes`. Any subclass of `RiceBubble::Attributes::Base` located there will automatically be found by the serializer. ```ruby class RiceBubble::Serializer::Wrap < RiceBubble::Attributes::Base def initialize(with: %w([ ]), &) super(&) @before, @after = with end def coerce(value) "#{before}#{value}#{after}" end end class WrappingSerializer < RiceBubble::Serializer attributes( name: wrap(with: %w({ })) ) end WrappingSerializer.call({ name: 'Spicy Beef' }) # => { name: '{Spicy Beef}' } ``` ### Fetching values from an object By default, attributes know how to fetch their values from the object being serialized by either (in this order): - invoking an instance method with the same name on the serializer; or - invoking an instance method with the same name on the object; or - looking up a hash key on the object ```ruby class FullNameSerializer attributes( full_name: string ) def full_name [object.first_name, object.last_name].join(' ') end end ``` You can also change how a value is retrieved by passing a block to the attribute: ```ruby class FullNameSerializer attributes( full_name: string { |o, _| [o.first_name, o.last_name].join(' ') } ) end ``` The block takes two values: the object being serialized, and (optionally) the name of the key being retrieved. ## 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitLab at https://gitlab.com/fauxparse/rice_bubble. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://gitlab.com/fauxparse/rice_bubble/blob/main/CODE_OF_CONDUCT.md). ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct Everyone interacting in the RiceBubble project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://gitlab.com/fauxparse/rice_bubble/blob/main/CODE_OF_CONDUCT.md).