Mark Swardstrom
Mark Swardstrom

Reputation: 18080

Create an ActiveRecord Instance from Json

What is the best way to create a new Ruby/Rails object without triggering any database actions?

For example if I have a Task class

class Task < ActiveRecord::Base
  has_many :tags
  has_one :location

end

And I want to create one from cached data that looks something like this

task_json = { 
  id: 1, 
  title: 'My Task', 
  tags: [ 
    { title: 'Tag1' }, 
    { title: 'Tag2'} 
  ]
  location: { lat: 46.0, lon: -120.1 } 
} 

And I want to reconstitute the object, but not trigger any database interactions.

@task = Task.new
@task.id = task_json['id']
@task.title = task_json['title']
@task.location = Location.new(:lat => task_json['location']['lat'], :lon => task_json['location']['lon'])

@task.tags = ...

Ideally, there would be no chance of saves or database interaction in any way once the object is built.

Upvotes: 4

Views: 10885

Answers (2)

Gary S. Weaver
Gary S. Weaver

Reputation: 8096

What is the best way to create a new Ruby/Rails object without triggering any database actions?

For example if I have a Task class

class Task < ActiveRecord::Base

Plain Old Ruby Objects

If you want a PORO (plain old Ruby object) that does not persist, why even subclass ActiveRecord::Base? You could just put this in any autoloadable path like app/models/task.rb:

class Task
  attr_reader :tags, :location, ...
  def initialize(...)
    @tags = ...
    @location = ...
    ...
  end
end

Then create it elsewhere like:

task = Task.new(...)

And you can then access its parts like:

task.tags

Where you either pass in the parsed JSON, the JSON string, args, etc.

However, the hash itself from JSON.parse(some_json_string) is a Ruby object, so you could just as easily use it. It won't trigger any DB operations either. :) The main reason you might use a class like Task is to make it a little more obvious what it is and what to expect from it, but a hash is easy enough to work with also. Just depends.

If you really want a read-only ActiveRecord model, you could use one of the several gems out there for it. I wrote one that a few others have contributed to called activerecord-be_readonly, but the times you'd need something like that should be few and far between, and it is more geared towards reading data from the DB and then attempting to not allow DB updates to that specific table via that model.

Only Making ActiveRecord Model Instance Read-only When Created From JSON

In your comment, you said you only want to make it read-only if pulled from cache, if I understand correctly.

Take a look at how the activerecord-be_readonly gem is altering AR behavior here: https://github.com/garysweaver/activerecord-be_readonly/blob/master/lib/activerecord-be_readonly/model.rb

I'm guessing you'll want to set a flag when you initialize the model with a JSON hash that similar methods could look at and raise ActiveRecord::ReadOnlyRecord or similar when that flag is set.

This only protects the model that you use these methods in though. If you have associations to other models, it will not protect them unless they use a similar method. In the end, you may be better off with POROs and regular AR models, if you absolutely must do this.

However, this sounds like it is going to be a pain to use. I'd avoid anything that makes the user (or developer) scratch his/her head too much.

Upvotes: 2

user1269636
user1269636

Reputation:

You can try to use JSON.parse on your JSON string task_json this way:

task = Task.new(JSON.parse(task_json))

Upvotes: 8

Related Questions