Cache data from external api with rails or ember? - ruby-on-rails

I have an ember/rails application with three main templates, Home, Contacts, and Payments. Both the contacts and payments templates need an array of contacts. This array needs to be populated from an external api. Currently, for the contacts every time I am going to this template the external api is being hit. Ideally I would like to hit the api asynchronously when a user first signs in, grabs this data once and can refer back to it without hitting the external api until necessary.
With rails I could easily just add has_one :contacts_list for user with a postgres json column and when switching the templates conditionally refresh this whenever needed. I am curious as to the best way to deal with this problem in ember.

You should create a DS.Model for 'contact' and use a has_many on Contacts and Payments model. Then, you could specify 'async: true' like
DS.hasMany('contact', {async: true}),
This will load the contacts asynchronously if they have not been loaded already. If they have been loaded, it will simply return the loaded contacts.
I am assuming that you have the following models: Contacts, Payments.

I've used the setupController method on a route to do this type of caching. Just check to see if the content is already available, and only load it if it's not there. Something like this:
App.ContactsRoute = Ember.Route.extend({
setupController : function(controller,model){
if(!controller.get('content')){
this.store.find('contact').then(function(contacts){
controller.set('content',contacts)
});
}
}
});

Related

Implementing Pusher Chat in Rails and React

I'm using this repo to create a chat system between 2 users in a Rails and React project. I've been able to log the user input into the console, and I have created messages_controller and message_threads_controller according to the repo.
However, I'm unable to persist the message to Rails db and then authenticate a user with Pusher before sending it to Pusher. Mainly because the from_uid, to_uid and thread_uid are not present by the time the message is been sent to Rails. Sending the message to rails like this:
sendMessage = (event) => {
event.preventDefault();
const {message} = this.state;
axios.post('/api/v1/messages', {message: message})
.then((response) => {
console.log(response);
})
console.log('send Message')
this.setState({'message': message});
console.log(this.state.message);
}
In my routes.rb file I have this
resources :messages
get 'threads/:id', to: 'message_threads#index'
post '/pusher/auth', to: 'pusher#auth'
I'm missing some required parameters, this is the error I get.
Pusher::Error - Bad request: Missing required parameter:
The flow according to this tutorial is that the message needs to be persisted first by the rails database before sending it to Pusher.
My question now is how do I produce the extra parameters (from_uid, thread_uid, to_uid) being used on the React side of the app here, to enable messages to be created?
Also, how do I authenticate the user using Pusher?
According to this Stack Overflow link they are getting from Rails the CSRF value like this - csrf = $('meta[name=csrf-token]').attr('content'). But I could not implement the same in React.
Answer from the author of the git repo.
The example I created here was pretty bare bones but below are a few bullet points that I hope will explain how you could expand on it.
Add a User model and Thread model to the Rails app
When a User is created, generate a public UID for the user (you can use Ruby's built-in SecureRandom.uuid to generate the id) and save that to the DB. This would become the ID for that user that you would expose in your javascript to allow for communications between users. All users would have a UID.
When a Thread is Created, generated a thread UID this would become the unique id for the conversation taking place
Add a Users_Threads has_and_belongs_to_many relationship so that you can eventually track the users that are subscribed to which threads
When React app loads, use an Ajax request to get a list of the current User's friends (returns list of user details + their uid) and a request to get all threads for current User
So let's say for example a User named Fred clicked on a User named Bob and wanted to send Bob a message but they do not currently have a thread. Fred types the message, clicks Submit and you send an Ajax request containing the message text, from_uid (Fred) and to_uid (Bob) with thread_uid as null (since there is no existing convo and no existing thread).
Your Rails app then receives that requests at a controller and sees that Fred is trying to send Bob a message and the thread ID is null, so the controller create a new thread (with its own UID) and then add two entries to users_threads one for the new thread_uid and bob's uid and another for the new thread_uid and Fred's uid. After that, you'd create a Message with that thread_uid and the participant details.
You'd also probably want users to see that they are part of a new thread without having to reload the page so you'd I think you'd want a Pusher channel subscription just for notifying users of a new thread. So I'd say in the UserThreads model after create a new thread you could send something like Pusher.trigger('threads_channel', user_secret_uid, { thread: new_thread_uid }). You'd also need to make sure in the react app that each user subscribes to the threads_channel for their user_secret_uid. For security, i'd make sure this is a different uid than the messaging otherwise people could subscribe to a list of a different users threads.

Creating a User Time Sheet for a RoR Web App

I'm kind of a newb to RoR and I'm working on creating my first web app.
So...My question is, how do I create a user time sheet in RoR?
What I need to do is create a classroom time sheet for students' (Users) reading times at home.
The students (Users) are able to sign up and have a profile created. From there, I would like for them to have access to log in their reading time(s).
I have attached examples of just some simple timesheets that would work perfectly for this.
I just do not know where to start and have not been able to find any gems that could help me create this.
Time Sheet 1
TimeSheet 2
Users: Ruby part
Use Devise gem - it will save a lot of time for you.
Add Devise to user model (something like that: rails generate devise User), then autogenerate basic Devise pages (sign in, sign up, etc), see Devise tutorials:
https://launchschool.com/blog/how-to-use-devise-in-rails-for-authentication
http://guides.railsgirls.com/devise
Also you'll probably need something like Job model with fields user_id, time_spent, date or something.
Timesheets: JS part
Time tracking is more front-end part of work, so you'll need to write some JS scripts for time tracking, which will monitor user activity and then send it to Job mobel on Rails side.
Track time spent on page (example):
var page_opened;
$(document).ready(function () {
page_opened = Date.getTime();
$(window).unload(function () {
page_closed = Date.getTime();
$.ajax({
url: "/save_user_time",
data: {
'timeSpent': page_closed - page_opened,
'job_id': job_id
}
})
});
}
Also you defenetly should take a look on some basic Rails tutorials for better understanding:
http://guides.rubyonrails.org/getting_started.html
https://www.codecademy.com/learn/learn-rails

How to securely register non-routing clicks on page via AJAX/update action?

I want to register various clicks on a webpage, e.g. toggle visibility of elements.
The clicks come from users not logged in.
I have an Impression model with an actions attribute that stores key-value pairs of actions made on a certain page.
On a click event I'm updating a record with this function:
function sendAjax(id, data) {
$.ajax({
type: "PATCH",
url: '/impressions/update',
data: {'impression_id' : id, 'actions' : data},
success: function(events){
}
});
}
But I'm realizing that this is not secure at all, the user could theoretically update whatever record she wants.
How could I do this more securely, can I take advantage of Rails' protect_from_forgery in any way with my use case?
I don't think this is a necessary feature. Maybe you think too much :)
Even in Google Analytic you can't stop a visitor from manipulating his action, theoretically. One can push any events he want just in console.
Also it's not necessary for analytic tool to be 100% secure and precise. There must be noises, you can ease them but can't really avoid them, or avoid them in a reasonable cost.
You just need to find the Impression from impressions belongs to the current_user.
Example:
def update
#impression = current_user.impressions.find(params[:impression_id])
# ...
end

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

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.
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)}
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.
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
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)

Generating secret URLs for a resource in rails

I've created a task management app that consists of lists and tasks. Users can only view their own lists and tasks. I would like to add the ability for a user to share a list if they like. Here are the steps I would like to accomplish:
User clicks a link from /list/show to share the list
User receives a secret URL to share: myapp.com/lists/1/23534512345234523 or whatever.
Secret URL redirects to a view other than /lists/show. Something like /lists/1/23534512345234523 which would be routed to /lists/secret_show or whatev.
Only users who have that url can see the information on that page.
Hope that is making sense. I imagine I would have to update the list record with a unique token to list.token. Then I would some how have to recieve the incoming URL and through a new action
lists#secret_share
def secret_share
...
end
Where I filtered for the list record by list.token and routed to secret_share. Then perhaps in the view I could simply restrict the view by the presence of the token in the URL.
Thoughts?
Whatever "secret URL" you hand out should not redirect to the real URL or you're going to create all kinds of opportunities for information leakage. It should be a strictly alternate URL.
Using routing for this seems like a good idea instead of using a separate controller. In your route you might want to pass an additional parameter to indicate this is a "secret" URL, like :secret => true where the value in question is something that cannot be submitted by the user to fake things out. User parameters are always strings, for instance, so using true should be a safe alternative.
This special parameter might disable access checking on your controller so that the page can be viewed by people that don't normally have access. You could also show a different layout using the layout method in your controller.

Resources