I'm trying to update all market data from an API call.
I have a Platform that contains many Markets. The markets have the high, low, latest price, etc.
I can't iterate through the associated collection and call a method to update. Maybe I have the whole structure incorrect, I'm not sure.
I thought it made sense to use the market.update method to refresh the data with an API call.
class MarketsController < ApplicationController
def update
#platform = Platform.find(params[:platform_id])
#market = #platform.markets.find(params[:id])
#market.api_get_market_summary
#market.save
redirect_to #market.platform
end
Which works fine in the Platform view
<% #platform.markets.each do |market| %>
<%= market.market_name %>
<%= market.high %>
<%= market.low %>
<%= link_to 'Update', [market.platform, market],
:method => :put %>
<% end %>
I've tried every combination in the platform controller but I have no idea how I should be do this to update all the markets in the platform.
class PlatformsController < ApplicationController
def update
#platform = Platform.find(params[:id])
#platform.markets.each do |market|
market.update(:id => market.id) # this obviously doesn't work
end
redirect_to #platform
end
Should I be updating all the attributes here with the update_attributes function?
I call the market API update when the object is created so the data gets initialized there which is great.
How should I go about this?
Another part, if I added another platform, how would I handle the different API requests this one would use?
Having the following relationship Platform --- has_many --- Market, if you want to perform an action on the collection of markets, have you considered adding a callback on the Platform model?
class Platform < ApplicationRecord
has_many :markets
after_save :update_markets
...
private
def update_markets
markets.each do |market|
...
end
end
end
Notice that:
after_save runs both on create and update, but always after the more specific callbacks after_create and after_update, no matter the order in which the macro calls were executed.
I'm not sure what are you trying to do here market.update(:id => market.id) but if you're updating only one record on all the markets consider update_all here's a good source
Another part, if I added another platform, how would I handle the different API requests this one would use?
By adding another platform a new Platform object is created and stored with a different ID, when the request hits your controller:
#market = #platform.markets.find(params[:id])
Also, consider #market = Market.find(params[:id]) instead of the above.
You can add this to your Platform model
accepts_nested_attributes_for :markets
Related
I am trying to create a simple data puller from the yahoo finance gem. I have a nested route setup for Security that has_many Price
The Price controller is as follows:
class PricesController < ApplicationController
before_action :set_security
before_action :set_price, only: [:show, :edit, :update, :destroy]
def new
#price = Price.new
end
private
def set_price
#price = Price.find_by_name(params[:id])
end
def price_params
params.require(:price).permit(:date, :open, :high, :low, :close, :volume, :security_id)
end
def set_security
#security = Security.find_by_ticker(params[:security_id])
end
end
The nested route works fine to manually create the child Price record from a form. I'm trying to create a Price record for each result in the array that gets generated from the following code:
class Datapuller
def self.download_historical
yahoo_client = YahooFinance::Client.new
data = yahoo_client.historical_quotes("FB")
data.each do |i|
#ticker = Security.find_by_ticker(data[0].symbol )
price = #ticker.prices.new()
price.security_id = #ticker
price.date = data[0].date
price.open = data[0].open
price.high = data[0].high
price.low = data[0].low
price.close = data[0].close
end
end
end
I'd like to call this method from a button link but for now I have the following code on the Security View:
<%= Datapuller.download_historical %>
This also give me the benefit to see the data that is getting loaded from the array.
With the above code I can create a child Price to a parent Security using a form and I can make the call from the yahoo finance gem. However, the array is not creating a Price record for each line in the array.
What am I missing in this?
Thanks!
The main reason why you aren't getting anything saved in your array of #ticker.prices is because you're instantiating them via price = #ticker.prices.new() but you're never saving them. You need to call the following after you set all the attributes:
price.save
to get that record
saved via Active Record and
associated with your #ticker.
If you skim through the Active Record basics guide it will help a ton to understand the life cycle of a record (save, validate, delete, etc).
You also will probably want to look at making your views and routes more resourceful, which is the Rails default. If you use a resource :prices in your routes, Rails will use it's convention to help you along the way.
So for instance, you'd have a create method on your controller that would solely be in charge of calling your Datapuller.download_historical - and that way you can make a button that links to that route just by saying <%= link_to 'Create Price', prices_path, method: :create %> that would create your prices on click. There are more ways to further let Rails help you write less code but that's a start. If you want more reading, I think M. Hartl does a great job of explaining RESTful resources in his intro guide.
questions_controller.rb
def index
#questions = Question.all(app_params)
end
private
def app_params
params.require(:questions).permit(:question, :answer)
end
end
question.rb
class Question < ActiveRecord::Base
end
I am completely new to ruby-on-rails. I was following a guide and it said I should take care of some "loopholes" or "security issues" and it used attr_accessible, but on Rails 4, they suggest strong parameters, so now I'm trying to use them. I'm confused on how to define the :questions params, because I'm currently getting an error saying that :questions param is not found.
:questions is pretty much something that I will define myself as the web developer.
So for example, I will define questions = "How are you?", "What is your name?". I'm basically starting very simply. I want questions that I have created to be displayed on my webpage. Ultimately, I plan to make a website what is basically a list of questions and, with answer options. After the user clicks "submit" I want to store the information into my database.
Am I supposed to even be requiring this as a param? I'm completely lost..
Do you have a dump of the params we could look at? They are shown when your app encounters an error, and typically shows you the params array which rails will pass through
Strong Params In Rails 4
Strong Params allow you to allow certain parameters for use in the controller, protecting against any malicious assignment client-side. They replaced attr_accessible in Rails 4.0
Strong Params is only for user-submitted content, as it's designed to protect the params hash. To that end, it's mostly used with the create and find functions:
class PeopleController < ActionController::Base
# Using "Person.create(params[:person])" would raise an
# ActiveModel::ForbiddenAttributes exception because it'd
# be using mass assignment without an explicit permit step.
# This is the recommended form:
def create
Person.create(person_params)
end
# This will pass with flying colors as long as there's a person key in the
# parameters, otherwise it'll raise an ActionController::MissingParameter
# exception, which will get caught by ActionController::Base and turned
# into a 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap { |person|
person.update!(person_params)
}
end
private
# Using a private method to encapsulate the permissible parameters is
# just a good pattern since you'll be able to reuse the same permit
# list between create and update. Also, you can specialize this method
# with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end
params.require
The params.require function works by taking this params hash:
params{:question => {:question => "1", :answer => "5"}}
That's why people asked what your params hash looks like, because the require function can only work if the :question hash is present.
Possible Solutions For You
Question.all(app_params)
Regardless of what you're trying to achieve, don't use all. The where function is better for receiving an array of data based on certain values. I believe all is depreciated anyway.
def index
#questions = Question.where("value = ?", variable)
end
What data is being passed?
I will define questions = "How are you?", "What is your name?"
This is okay, but typically in rails, you'd call data by using an ID in the database. If you're defining these questions in a form, you'd use the strong params system; but you'd need a form to submit the data to
Further Additions
The rails way is to keep all your data in a database, and use the application to manipulate that data, either by showing it, or allowing people to input more.
The "params" variables are basically there to help the rails controllers & models accept & process data from end users, and consequently allow you to keep the system growing. Instead of having to write custom code to accommodate all sorts of different data, the params give you a rigid structure to work with. Here is a good explaination of how MVC (and params) works for you: How does an MVC system work?
I think you're getting confused with how your app should work
Your "questions" should be stored in a questions table / model, and can be accessed by calling their ID's with the find function. This code would be like this:
#app/controllers/questions_controller.rb
def show
#question = Question.find(params[:id])
end
If you want to add new questions, you'll be best to add them to the questions table, like this:
#app/controllers/questions_controller.rb
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.save
end
private
def question_params
params.require(:question).permit(:question)
end
#app/views/questions/new.html.erb
<%= form_for #question do |f| %>
<%= f.text_field :question %>
<% end %>
This will give you a central store of your questions, which you'll then be able to access when you need them, either with a helper or with your ".all" call :)
Give it a shot with question (singular):
params.require(:question).permit(:text, :answer)
Assuming question is your model and text (which I made up) is the wording of the question.
I have a user class, which has_many resumes, each of which has many items. On my users/show page, I render multiple resumes, which is working. In my users_controller I have the following:
def show
...
#resumes = #user.resumes.paginate(page: params[:page])
#resume = #user.resumes.build if user_signed_in?
#resume_items = #user.res.paginate(page: params[:page])
#edu_items = #resume.edu.paginate(page: params[:page])
...
end
I defined the function res in my User model:
def res
Resume.where("student_id = ?", id)
end
And that worked quite well. However, I'm trying to do the same thing with the function edu in my Resume model:
def edu
Education.where("resume_id = ?", id)
end
but it's not working, #edu_items isn't being set to anything. Now I know it has to do with this method specifically, because if I change id to the id of a specific resume, that resume's items are rendered correctly, except across every resume. I know it's a simple fix, I've just been staring at it for way too long at this point and can't figure it out. Any advice would be amazing.
EDIT: #makaroni4: Instead of having #educations = #user.educations, I'd rather keep the items from each resume separate. Is it possible to define a method like the educations one that will make #educations = #resume.educations?
EDIT 2: I managed to get what I was trying to do to work, thanks for the advice. I solved it by doing away with the edu method altogether, and passing local variables to the partial:
<%= render :partial => 'shared/edu', :as => :educations, :locals => {:resume_educations => resume_item.educations} %>
shared/edu
<% if resume_educations.any? %>
<ol class="educations">
<%= render partial: 'shared/edu_item', collection: resume_educations %>
</ol>
<%= will_paginate #educations %>
<% end %>
Probably not the cleanest solution, but it seems to work.
I think that your model structure should look like:
class User < ActiveRecord::Base
has_many :resumes
def educations
Education.joins(:resume => :user).where(:users => { :id => id })
end
end
class Resume < ActiveRecord::Base
belongs_to :user
has_many :educations
end
class Education < ActiveRecord::Base
belongs_to :resume
end
So in your controller you can access them like:
#resumes = #user.resumes
#educations = #user.educations # all users educations, from all resumes
or
#educations = #resume.educations # educations for particular resume
And also I recommend you to read this article http://petdance.com/2012/04/the-worlds-two-worst-variable-names/ about variables naming, variables like resume_items and methods res and edu should say that you're doing smtg not in the right way.
It does not work, because the result of your edu method will always be empty.
In your code you are building a resume object:
#resume = #user.resumes.build if user_signed_in?
If you use build an object is created, but not saved to the database yet. This means that your #resume.id is nil. And thus the result of your edu method will be empty.
You could use the following to create the record in the database:
#resume = #user.resumes.create if user_signed_in?
But your edu method will still return an empty collection, because it's a new record and it won't be associated with any items yet.
Please expand on what you are trying to do exactly, because with this code #resume.edu will always be empty for the reason explained above.
Also: consider using the built-in Rails functionality instead of making your own methods.
Here's more of an academic question for you guys. Say I want to create a model in a ruby on rails app to track simple views information. I would like to record the user_id, the URI for the page viewed, and keep track of the number of times the user has visited a page.
Model A: One way to do this would be to create a model View with attributes user_id and page (records the uri), and then create a new entry every time a user opens a page.
Model B: A second way to do this would be to add an attribute "page_views" to the model, to track the number of times the user has accessed that page.
Pros and Cons: Model A would have more information recorded and lead to a larger db than Model B. However, Model B would require that a controller search for an existing user-page combination, and either add views to that entry, or create a new one. This leads to a smaller database, but may be worse in scale due to the need to search for existing entries.
So my question to you guys is: which is more important? Are my thoughts wrong? Am I missing something here (other performance considerations overlooked?)
NoSQL approach to tracking user activity:
model app/models/user.rb
class User < ActiveRecord::Base
include UserModules::Tracker
...
end
mixin app/models/user_modules/tracker.rb
module UserModules
module Tracker
def get
key = "user_" + self.id.to_s
arr = Resque.redis.lrange(key, 0, -1)
arr.map{|i| JSON.parse(i)}
end
def put(controller_name, action_name, details="")
key = "user_" + self.id.to_s
created = Time.now.to_formatted_s(:db)}.to_json
# silent exception handle, so you can do not run Redis localy
begin
Resque.redis.rpush key, {
:controller_name => controller_name,
:action_name => action_name,
:details => details,
:created_at => created
rescue
nil
end
end
end
end
controller app/controller/dashboard.rb
class Dashboard < ApplicationController
after_filter :track, :only => :show
# this action will be tracked
def show
end
# this action will show tracking
def logs_show
render :json => current_user.get
end
...
private
def track
details = "any details...."
current_user.put(controller_name, action_name, details)
end
end
You need to have Redis installed, I prefer to use Resque as common way to setup and initialize Redis via Resque.redis, because it will help you to browse your tracking with resque-web
On of the way to setup Redis is in my gist
I have an application with set limits on subscription attributes i/e a user can have five projects for subscription A but have ten for subscription B
At present I run a check on there current usage when linking to the create action and if they are over there limit I do not display the link to create a new project. On the view (for the new project ) I again run the check (in a helper) to see if they can create a new project and if they are not I display a message stating so and a little upgrade link.
Is this a secure method of stopping a user bypassing there subscription attribute limitations ?
What about direct PUT requests etc ?
You can also validate that the user's subscription allows starting a new project when a new project is created. This guarantee that even if they posted directly to the new_project_path they would get an error.
class Project
belongs_to :user
validate_on_create :subscription_allows_new_project
def subscription_allows_new_project
unless self.user.subscription.max_projects > self.user.projects.count
errors.add_to_base("Project limit reached, please upgrade today!")
end
end
end
If you're really cautious about the put requests, you could simply create a helper method that you call in all of the pages.
<% if has_user_hit_project_limits %>
Upgrade Now!
<% else %>
Add project
<% end %>
def has_user_hit_project_limits
if #logic
true
else
false
end
end