Rails 4 — How to populate a user model from JSON API?
Asked Answered
P

4

7

Firstly, I am new to rails so sorry if there is anything that I don't understand correctly. I am wondering how can I populate a model with data fetch thru an API.

Context: I am using a OAuth2 authentication with omniauth/devise.

In my User controller client-side (opposite to the provider), I fetched all users who logged in at least once is this "client app" and I want to display them. Obviously, anytime a new user logged in to the client app I don't store all his information in the client database to avoid duplicate. All I am storing is the user_id along with its access token.

Therefore, I was thinking that after fetching all users data I could populate them to a user model before passing it to the view. What would be the best way of doing such a thing? I was looking into the Named Scope but it is not clear to me how to apply it to my particular scenario. For instance, it would be great to be able to fetch an populate all users in my model using something like this:

# ApiRequest is a container class for HTTParty
get = ApiRequest.new.get_users(User.all) // here "get" is a JSON array of all users data
response = get.parsed_response['users']
User.populate(response).all # would something like this possible? If yes, is Named Scope the appropriate solution?

Thank you very much in advance for you help.

Paramaribo answered 13/12, 2013 at 15:46 Comment(1)
A named scope is only a shortcut for different query conditions, something like: scope :only_active, where(active: true) used by calling MyModel.only_active which returns the list of MyModel objects respecting the condition active = true. It has nothing to do with attribute assignment.Plenum
M
12

Let's say that response is an array of attribute hashes:

[{'id' => 1, 'name' => 'Dave'}, {'id' => 2, 'name' => 'Bill'}]

You can map this into an array of Users with:

users = response.map{|h| User.new(h)}
Maggi answered 17/12, 2013 at 1:40 Comment(2)
+1 my answer was not having in consideration that data contains multiple Users, now fixed inspired in this one.Joscelin
+1 && I would add a security feature here since it's using an API that could bring some bad user input: users = response.map{ |h| User.new( h.slice(*User.column_names) ) } -> This only authorises the Attributes of the User model.Plenum
J
2

If you don't want to touch database, and also want to populate virtual attributes I think the only way is to implement your own populate method:

# The User class
def self.populate(data)
  data.map do |user_json|
    user = User.find(user_json[:id])
    user.assign_attributes(user_json)
    user
  end
end

Check the assign_attributes documentation for security advices.

Joscelin answered 16/12, 2013 at 11:21 Comment(3)
Thanks. Quick question, what's the point of the array here as it will be overridden for each call to the populate method?Paramaribo
The Users are populated temporally, the array with the temporally populated Users is returned so you can use them in the view. assign_attributes is not touching database.Joscelin
I'm going to assume that you meant temporarily, and that you are not suggesting any kind of time travel solution :)Maggi
A
2

The simpliest way is to use active_resource http://railscasts.com/episodes/94-activeresource-basics

P.S. In Rails 4 it's gone to gem https://github.com/rails/activeresource

Antipasto answered 17/12, 2013 at 9:28 Comment(0)
J
1

I don't find a direct way to do it, the shortest solution I can suggest is using the method update:

ids = get.map{ |e| e["id"] }
User.update(ids, get)
Joscelin answered 13/12, 2013 at 16:49 Comment(1)
Thanks. The problem is that I want to populate virtual attributes, which shouldn't require to alter or "update" the database.Paramaribo

© 2022 - 2024 — McMap. All rights reserved.