みずラテ

牛乳と水を2対1で。

Rails - Create Object from nested hash of JSON data by dry-struct

Now I am wondering to migrate a gem virtus to other new gem. And I tried dry-struct and dry-types. Here is the result of creating Rails Model Object from nested hash of JSON data.

What I would like to do

I prepared a sample data as below. The data is like JSON data and it has nested struct. Then the final expected result is to have auther object(Auther Class) and books objects(Array[Book Class]).

json_data = <<EOS
{
  "status": "0",
  "message": "",
  "author": {
    "id": "auther111",
    "name": "akutagawa"
  },
  "books": [
    {
      "id": "book111",
      "name": "rasho-mon",
      "publishedyear": "1915"
    },
    {
      "id": "book222",
      "name": "kumonoito",
      "publishedyear": "1918"
    },
    {
      "id": "book333",
      "name": "ababababa",
      "publishedyear": "1923"
    }
  ]
}
EOS

My environment

Rails 6.0.2
Ruby 2.5.7

Install dry-struct

At first, let's install gem dry-struct. It will install other related gems such as dry-types.

gem 'dry-struct', '~> 0.5.0'

bundle install

Initial setting

follow the way that the official document instruct.
dry-rb.org

create new file types.rb at initializers like below.

module Types
  include Dry.Types()
end

Create Classes

Create three classes Author, Book, and GetAuthorInfo. All of them has to be inherited by Dry::Struct.

class Book < Dry::Struct
  attribute :id, Types::String
  attribute :name, Types::String
  attribute :publishedyear, Types::String

  def title_call
    "The name of the book is" + name + "."
  end
end

class Author < Dry::Struct
  attribute :id, Types::String
  attribute :name, Types::String

  def my_name
    "My name is" + name + "."
  end
end

class GetAuthorInfo < Dry::Struct
  attribute :status, Types::String
  attribute :message, Types::String
  attribute :author, Author
  attribute :books, Types::Array.of(Book)
end

Usage

Let's use it. When get JSON data and parse it to hash(need to symbolize it) and put it to initial value of the GetAuthorInfo class, the nested object will be created!

parsed_data = JSON.parse(json_data, symbolize_names: true)
autherInfo = GetAuthorInfo.new(parsed_data)
autherInfo.auther.myname

Ofcourse you can call method of the class from the object. Try more.

autherInfo.books.size
autherInfo.books[1].title_call

Wonderfull!

Thank you for reading by post.