eliot
eliot

Reputation: 1329

Rails Transform Json Request

I am creating an API for serving up crossword puzzles. I am trying to stay pretty close to the JSON format proposed at XwordInfo. What I am having trouble with is transforming how I want it described in my models (client and server side) and how I want it in the database.

For example, take clues:

{
  ...

  "clues": {
              "across": [
                           "1. This is a clue",
                           "5. So is this"
              ], 
              "down": [
                         "1. This is a down clue",
                         "2. Here we go again."
              ]
           }
  ...
}

Client-side puzzle model:

...
String title
Dictionary<String, Array> clues
String authore
...

Database:

create_table "clues", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "direction"
    t.integer  "crossword_id"
  end

  add_index "clues", ["crossword_id"], name: "index_clues_on_crossword_id", using: :btree

So I need to convert basically an array of clue name, direction objects (what's in the database) to a dictionary of clues which has 2 arrays, across and down. This seems like something that should be fairly common to have to do (transforming requests/responses) but I can't quite figure out how to search for it.

EDIT: I've already got the serialization part figured out with JBuilder: Here is how I display an array of clues:

crossword/index.json.jbuilder:

json.clues do
  json.across @crossword.clues.reject { |clue| clue.direction != 'across' }.collect { |clue| clue.name}
  json.down @crossword.clues.reject { |clue| clue.direction != 'down' }.collect { |clue| clue.name}
end

But coming from the user, how do I take that same dictionary > of clues and turn it into a list of Clue(name, direction,

Upvotes: 0

Views: 174

Answers (2)

Shadwell
Shadwell

Reputation: 34774

I think you're just talking about iterating over the hash structure. Something like:

crossword = Crossword.find(id) # Or somesuch - not sure how you get the crossword

# Process across then down clues from the clues hash
["across", "down"].each do |direction|
  # Iterate over the clues in the array for the given direction
  clues[direction].each do |clue_text|
    # Create a clue on the crossword with the given text and direction
    crossword.clues.create!(name: clue_text, direction: direction)
  end
end

That will certainly populate your clues model from the json. Providing of course you have a has_many clues on your Crossword model (you seem to).


Aside 1: You might want to extend the model to include the number of the clue though because that implementation will not guarantee your clues are returned in order. This will obviously affect your serialization/deserialization though and you may end up with a hash for each clue. Or may it's enough to parse the clue text for the number.

Aside 2: You may also want to add a couple of direction class methods to your Clue class to make it easier to retrieve down and across clues:

def self.down
  where(direction: 'down')
end

def self.across
  where(direction: 'across')
end

In your jbuilder you can then do:

@crossword.clues.down.collect(&:name)

Upvotes: 1

Brett Banks
Brett Banks

Reputation: 170

This is a great use case for a serializer. I like https://github.com/rails-api/active_model_serializers

You can create a serializer for the Crossword model that in turn will return an array of its clues, each of which is serialized by its own Clue serializer.

Upvotes: 1

Related Questions