Rails - submitting JSONs to database from controller - ruby-on-rails

I am working on a Rails app, and I am attempting to insert attributes from JSONs as database entries. I'm running into a problem, though, and would appreciate some guidance.
I've been able to jam a few things together and come up with something that sort of works...
def create
#report_group = Array.new
#report_group.push({location:"home", comments:"Hello, database!"}, {location:"away", comments:"Goodbye, database!"})
#report_group.each do |x|
#new_report = Report.new(x)
#new_report.user_id = current_user.id
#new_report.save
end
end
private
def report_params(params)
params.permit(:user_id,:location,:comments)
end
This is a good first step - this commits two entries to my database, one for each of the hashes pushed into #report_group, but it is suffering from a problem - the create action does not reference the report_params whitelist.
I have built several Rails apps where entries are submitted one at a time via the standard Rails form helpers, but I have never done it with multiple JSONs like this before. Trying out the syntax I'd use in a typical form helper situation
#new_report = Report.new(report_params(x))
throws the expectable error undefined method permit' for #<Hash:0x007f966b35e270> but I am not sure what else to do here.
EDIT TO SHOW SOLUTION
Big thanks to #oreoluwa for pointing me in the right direction. Here's the solution that I came up with.
def create
#report_group = Array.new
#report_group.push({location:"home", comments:"Hello, database!"}, {location:"away", comments:"Goodbye, database!"})
#report_group.each do |x|
hash = ActionController::Parameters.new(x)
#new_report = Report.new(report_params(hash))
#new_report.user_id = current_user.id
#new_report.save
end
end
private
def report_params(params)
params.permit(:user_id,:location,:comments)
end

You're getting the error because a Hash is not the same as an ActionController::Parameters. In order to use the permit method with your Hash you may need to first convert it to ActionController::Parameters, as such:
hash = {location:"home", comments:"Hello, database!"}
parameter = ActionController::Parameters.new(hash)
parameter.permit(:user_id,:location,:comments)
I don't know if that is what you're looking for, but I thought to point you in the right direction.

Related

DRY way of assigning new object's values from an existing object's values

I created a class method that is called when a new object is created and copied from an old existing object. However, I only want to copy some of the values. Is there some sort of Ruby shorthand I can use to clean this up? It's not entirely necessary, just curious to know if something like this exists?
Here is the method I want to DRY up:
def set_message_settings_from_existing existing
self.can_message = existing.can_message
self.current_format = existing.current_format
self.send_initial_message = existing.send_initial_message
self.send_alert = existing.send_alert
self.location = existing.location
end
Obviously this works perfectly fine, but to me looks a little ugly. Is there any way to clean this up? If I wanted to copy over every value that would be easy enough, but because I only want to copy these 5 (out of 20 something) values, I decided to do it this way.
def set_message_settings_from_existing(existing)
[:can_message, :current_format, :send_initial_message, :send_alert, :location].each do |attribute|
self.send("#{attribute}=", existing.send(attribute))
end
end
Or
def set_message_settings_from_existing(existing)
self.attributes = existing.attributes.slice('can_message', 'current_format', 'send_initial_message', 'send_alert', 'location')
end
a hash might be cleaner:
def set_message_settings_from_existing existing
fields = {
can_message: existing.can_message,
current_format: existing.current_format,
send_initial_message: existing.send_initial_message,
send_alert: existing.send_alert,
location: existing.location
}
self.attributes = fields
end
you can take this further by only selecting the attributes you want:
def set_message_settings_from_existing existing
fields = existing.attributes.slice(
:can_message,
:current_format,
:send_initial_message,
:send_alert,
:location
)
self.attributes = fields
end
at this point you could also have these fields defined somewhere, eg:
SUB_SET_OF_FIELDS = [:can_message, :current_format, :send_initial_message, :send_alert, :location]
and use that for your filter instance.attributes.slice(SUB_SET_OF_FIELDS)

Ruby on Rails beginner question : equality

I'm starting to know ROR and I was doing a kind of blog with articles, etc...
I did this code :
def show
id = params[:id]
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
As you can see, this code just wants to check if the article ID exists or not. So I'm testing equality between id and article.id (which's a model linked to the appropriated table in the database) BUT when I try to use or display #is_valid boolean I saw that article.id == id is FALSE every time, even if article.id = 2 and id = 2. I tried to think about everything that can make this occuring, but I admit I still misunderstand this.
Then I ask you if you know why this is occuring. Of course, an equality like 2 == 2 will change #is_valid to true.
Thank you for your help !
Maybe its because params[:id] it's a string and article.id it's an Integer
(byebug) params
{"controller"=>"admin/my_controller", "action"=>"edit", "id"=>"1"}
And yes it is... "id" is a string "1", so you may try this:
def show
id = params[:id].to_i
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
end
And maybe could work.
This is the answer to your question,
But if you want to learn a little more about Activerecord you can do this
Article.exists?(params[:id])
and that will do what you are trying to do just with a query against db.
and if you want to get just a simple article
record = Article.find_by(id: params[:id]) #return nil when not exist
if record # if nil will threat like false on ruby
#my code when exist
else
#my code when not exist
end
will work (you also can use find but find will throw an exception ActiveRecord::RecordNotFound when not exists so you have to catch that exception.
Activerecord has many ways to check this you dont need to do it by hand.
def show
#article = Article.find(params[:id])
end
This will create a database query which returns a single row. .find raises a ActiveRecord::NotFound exception if the record is not found. Rails catches this error and shows a 404 page. Article.find_by(id: params[:id]) is the "safe" alternative that does not raise.
Your code is problematic since list = Article.all will load all the records out of the database which is slow and will exhaust the memory on the server if you have enough articles. Its the least effective way possible to solve the task.
If you want to just test for existence use .exists? or .any?. This creates a COUNT query instead of selecting the rows.
Article.where(title: 'Hello World').exists?

Displaying from my database

I'm new to rails and I'm working on a project where I'm having an issue. I'm trying to display all the gyms that have the same zipcode. When I tried the code below, it only displays 1 and not the other ones. How can display all the gym that have the same zip code?
controller
def gym
#fitness = Fitness.find_by(zip_code: params[:zip_code])
end
gym.html.erb
<%= #fitness.name %>
You're doing this to yourself. By definition, #find_by only returns a single record, or nil. You probably want #where instead:
Fitness.where(zip_code: params[:zip_code])
If that still doesn't work, check both your table data and the content of your params hash to make sure you're creating a valid query.
def gyms
#fitness = Fitness.where("zip_code = ?", params[:zip_code])
end

Is Create method or Update faster?

I'm using rails 3.2.11 and ruby 1.9.3.
I have a slow page and I know I have many ways to optimize it. Currently I am focused on the method update_attributes.
Here is my code:
def create
#user = current_user
#demo = #user.demos.new
race_ethnicity_response = []
params[:race_ethnicity_response].each do |response, value|
race_ethnicity_response << response if value != '0'
end
params[:demo][:race_ethnicity_response] = race_ethnicity_response.join(', ')[0, 254]
#demo.update_attributes(params[:demo])
end
Or should I use something like build and save or create?
#demo = #user.demos.build
...
#demo.save!
Or
#users.demos.create!(params[demo])
I am curious which is faster. I know if it save 2ms then I should use the one which is more code correct/readable.
On a small operation like this you aren't going to see much of a performance difference. Go for readability + maintainability. The code above does seem a little scattered, particularly the middle block. Here is a straightforward approach although I may be missing something related to the params[:race_ethnicity_response] loop.
#demo = Demo.new(:params)
#demo.race_ethnicity_response = race_ethnicity_response.reject{|i| i == 0 }.join(', ')[0, 254]
current_user.demos << #demo

Ruby on Rails -- NameError '#' not allowed as an instance variable name

I am fairly new to Ruby on Rails and have been stuck with this bug for quite sometime now. I am hoping that someone could give me some useful information to fix this bug. I have a feeling I am overlooking something trivial. Anyhow, I have included below my code that is drawing the Error (that is in the View) and also my code that is in my controller. While I have done my research before posting on here, I may have to put my instance variable in my controller -- however, I am unsure how that process goes and then how to call it from my View. I would appreciate any help! Thanks in advance :)
The error I get:
NameError in Search#indx
'#' is not allowed as an instance variable name
Here is my line of code that is drawing the error in my view (apps/views/search/index.html.erb):
<% #search = instance_variable_get("\##{params[:model].to_s.downcase.pluralize}")%>
Here is my code in my controller (apps/controllers/search_controller.rb):
class SearchController < ApplicationController
def index
#containers = Container.search(params[:q])
#cpus = Cpu.search(params[:q])
#disks = Disk.search(params[:q])
#firmwares = Firmware.search(params[:q])
#hardwares = Hardware.search(params[:q])
#hosts = Host.search(params[:q])
#interfaces = Interface.search(params[:q])
#lans = Lan.search(params[:q])
#licenses = License.search(params[:q])
#rams = Memory.search(params[:q])
#networks = Network.search(params[:q])
#products = Product.search(params[:q])
#roles = Role.search(params[:q])
#sites = Site.search(params[:q])
#vmpools = Vmpool.search(params[:q])
#trackings = Tracking.search(params[:q])
end
end
The problem is that params[:model] is nil, so you're essentially doing instance_variable_get("#").
Take a look at the Rails log and see if the params you think are being received are actually being received.
Edit: The below is not directly related to the problem you're having (the previous two sentences explain why you're having that problem), but rather a suggestion for improving the way your code is organized.
Doing instance_variable_get in a view is a pretty bad code smell. Really, doing instance_variable_get at all is a code smell. There's almost certainly a better way to do what you're trying to do. For example:
def index
query = search_params[:q]
#search_results = {
containers: Container.search(query),
cpus: Cpu.search(query),
disks: Disk.search(query),
# ...
}
model = search_params[:model].to_s.downcase.pluralize # <-- this is also pretty smelly
#search = #search_results[model]
end
private
def search_params
params.require(:q, :model)
end
Then in your view you can just access #search.

Resources