Gem Version example workflow Maintainability Test Coverage
Sumaki is a wrapper for structured data like JSON.
Since Sumaki wraps the target data as it is, rather than parsing it using a schema, the original data can be referenced at any time.
This makes it easy to add or modify definitions as needed while checking the target data.
This feature may be useful when there is no document defining the structure of the data, or when the specification is complex and difficult to grasp, and the definition is written little by little starting from the obvious places.
class AnimeList include Sumaki::Model repeated :anime field :name class Anime include Sumaki::Model singular :studio field :title class Studio include Sumaki::Model field :name end end end data = { name: 'Winter 2023', anime: [ { title: 'The Vampire Dies in No Time', studio: { name: 'MADHOUSE Inc.' } }, { title: '"Ippon" again!', studio: { name: 'BAKKEN RECORD' } } ] } anime_list = AnimeList.new(data) anime_list.name #=> 'Winter 2023' anime_list.anime[0].title #=> 'The Vampire Dies in No Time' anime_list.anime[0].studio.name #=> 'MADHOUSE Inc.' anime_list.anime[0].object #=> { title: 'The Vampire Dies in No Time', studio: { name: 'MADHOUSE Inc.' } }
Install the gem and add to the application's Gemfile by executing:
$ bundle add sumaki
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install sumaki
Include Sumaki::Model module in the class that wraps the data, and give the data when creating the instance.
class Anime include Sumaki::Model end Anime.new({})
Only this does not give access to fields or structured data, so the following declarations need to be added.
By declaring field, you can access the field.
class Anime include Sumaki::Model field :title field :url end
# Read the field values anime = Anime.new({ title: 'The Vampire Dies in No Time', url: 'https://sugushinu-anime.jp/' }) anime.title #=> 'The Vampire Dies in No Time' anime.url #=> 'https://sugushinu-anime.jp/'
# Write the field value anime = Anime.new({}) anime.title = 'The Vampire Dies in No Time' anime.title #=> 'The Vampire Dies in No Time'
If the data contains attributes not declared in the field, it raises no error and is simply ignored.
When a type is specified, it will be typecast.
class Character include Sumaki::Model field :age, :int end character = Character.new({ age: '208' }) character.age #=> 208
Types are:
:int:float:string:bool:date:datetime
By declaring singular, you can access the sub object.
class Book include Sumaki::Model singular :company field :title class Company include Sumaki::Model field :name end end
data = { title: 'The Ronaldo Chronicles', company: { name: 'Autumn Books', } } # Read from the sub object book = Book.new(data) book.company.name #=> 'Autumn Books'
# Build a sub object book = Book.new({}) book.build_company(name: 'Autumn Books') book.company #=> #<Book::Company:0x000073a618e31e80 name: "Autumn Books">
Sub object is wrapped with the class inferred from the field name under the original class.
This can be changed by specifying the class to wrap.
class Book include Sumaki::Model singular :author, class_name: 'Character' field :title class Character include Sumaki::Model field :name end end data = { title: 'The Ronaldo Chronicles', author: { name: 'Ronaldo' } } book = Book.new(data) book.author.class #=> Book::Character
By declaring repeated, you can access the repeated sub objects as an Array.
class Company include Sumaki::Model repeated :member field :name class Member include Sumaki::Model field :name end end
data = { name: 'The Ronaldo Vampire Hunter Agency', member: [ { name: 'Ronaldo' }, { name: 'Draluc' }, { name: 'John' } ] } # Read from the sub object company = Company.new(data) company.member.size #=> 3 company.member[2].name #=> 'John'
# Build a sub object company = Company.new({}) company.member.build(name: 'John') company.member[0].name #=> 'John'
The class_name option can also be used to specify the class to wrap.
Parent object can be referenced from sub object by #parent method.
class Character include Sumaki::Model singular :child field :name class Child include Sumaki::Model field :name end end data = { name: 'Draus', child: { name: 'Draluc' } } character = Character.new(data) character.child.name #=> 'Draluc' character.child.parent.name #=> 'Draus'
By declaring enum, You can map a field to the specified value.
class Character include Sumaki::Model field :name enum :type, { vampire: 1, vampire_hunter: 2, familier: 3, editor: 4 } end
data = { name: 'John', type: 3 } # Read the enum character = Character.new(data) character.type.name #=> :familier character.type.familier? #=> true
# Write the enum value character = Character.new({}) character.type = 1 character.type.name #=> :vampire character.type = :vampire_hunter character.type.name #=> :vampire_hunter character.type.familier! character.type.name #=> :familier
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.
Bug reports and pull requests are welcome on GitHub at https://github.com/nowlinuxing/sumaki.
The gem is available as open source under the terms of the MIT License.