Undefined method `keys' for nil:NilClass error on rails - ruby-on-rails

I tried to run web server and it shows the following error:
undefined method `keys' for nil:NilClass error on rails
Extracted source (around line #21):
shopsList = [st1, st2, st3, st4]
render :json => shopsList
end
end
Here are the files:
shop.rb
class Shop < ActiveRecord::Base
attr_accessor :name, :description, :comments
def initialize(name, description, comments)
#name = name
#description = description
#comments = []
end
end
comment.rb
class Comment
attr_accessor :id, :name, :content
def initialize(id, name, content)
#id = id
#name = name
#content = content
end
end
shops_controller.rb
class ShopsController < ApplicationController
def index
end
def shops
com1 = Comment.new("FX991", "Goat", "Delicious!")
com2 = Comment.new("F2888", "Cow", "Amazing!")
com3 = Comment.new("GH555", "Cat", "Yummm!")
com4 = Comment.new("HY666", "Fish", "Mouth watering!")
commentList = [com1, com2, com3, com4]
sh1 = Shop.new("AAAA", "Je", commentList[0])
sh2 = Shop.new("NNNN", "Te", commentList[1])
sh3 = Shop.new("CCCC", "Be", commentList[1])
sh4 = Shop.new("DDDD", "He", commentList[1])
shopsList = [sh1, sh2, sh3, sh4]
render :json => shopsList
end
end
When I tried changing render :json => shopsList to render :json => commentList, the comment list would show as json format in the server.
Also, is there something wrong with the way I access or declare the commentList array? The contents of the array won't show when I try to access it. It just displays "[]"

You need to pass a hash to render
try this
shopsList = [sh1, sh2, sh3, sh4]
render :json => {:success=>true, :data=>shopsList}

Can you please post the stacktrace?
I don`t think "render" is causing the error, I think it happens earlier on in the callstack.
#tested this, it is valid code
def mytest
data = ["1","2","3"]
render :json => data
end

Related

Rails API Does not split Json

Weird problem. If the class at the bottom was a module, split the Json without problems, if it was only methods, also works, but the problem is.. when it is a class, it does not split the Json anymore, and returns an empty array.. however, if being a class, I do a puts the object, it actually puts it..
Any thoughts about why? How can I fix it?
I have this controller:
def index
begin
call_employee_work_locations_api
rescue => ex
render :json => {"service unavailable": "0001" }, :status => :service_unavailable
end
end
I have this service:
def call_employee_work_locations_api
auth = {:username=>ENV["USERNAME"], :password=>ENV["PASSWORD"]}
employee_locations = HTTParty.get(employee_work_Location_url , :basic_auth => auth)
#serialize_work_location(employee_locations)
serializer = EmployeeSerializer.new
serializer.serialize_work_location(employee_locations)
end
I have this builder:
json.array!(#top_locations) do |location|
json.extract! location, :name, :description, :latitude, :longitude
end
I have this class:
class EmployeeSerializer
def serialize_work_location(employee_locations)
employee_locations= JSON.parse(employee_locations)
locations=[]
employee_locations["work_locations"].each do |attributes|
location = Location.new(attributes["latitude"],attributes["longitude"],attributes["description"],attributes["name"])
locations.push(location)
end
employee_locations_selector(locations)
end
def top_office_location_selector(locations, city)
top_locations=[]
locations.each do |office|
if office.name == city[0] then top_locations.push(office) end
if office.name == city[1] then top_locations.push(office) end
end
#top_locations = top_locations
p #top_locations <--- it prints the object perfectly, but does not pass to the view, I get an empty array instead.
end
def employee_locations_selector(locations)
city = locations.each_with_object(Hash.new(0)) { |locations, counts| counts[locations.name] += 1 }.max_by{|k,v| v}
top_office_location_selector(locations, city)
end
end
The instance variable #top_locations is being set within the scope of the EmployeeSerializer class, not your controller. As such it's just a normal instance variable and so Rails knows nothing about it. You can assign the return value of #top_office_location_selector to an instance variable in the controller and it should work.
On a side note, the code would be cleaned up a lot by using #map over #each.

Unknown attribute in Rails - creating new Object

I'm working on my Rails API. Currently I want to save an Object through post-requests from my Device. The requests are working fine, but there is a problem saving it to the DB. Rails says:
ActiveRecord::UnknownAttributeError (unknown attribute 'idea_id' for IdeaTag.):
app/controllers/data_controller.rb:42:in `update_ideas'
So, I know this means it can't find the attribute "idea_id" in "IdeaTag".
Heres my data_controller.rb
class DataController < ApplicationController
skip_before_filter :verify_authenticity_token
def token
name=params[:owner]
password=params[:password]
#owner = Owner.authenticate_by_name(name, password)
if #owner
if #owner.user_uuid.blank?
#user = User.new
#user.token = SecureRandom.uuid
#user.name = #owner.name
#user.owner_uuid = #owner.uuid
#user.created_at = Time.now
#user.updated_at = Time.now
#user.isLoggedIn = false
#user.save!
end
#user = User.find_by_uuid(#owner.user_uuid)
if #user.token.blank?
token = SecureRandom.uuid
#user.token = token
#user.save
end
else
render nothing: true, status: :unauthorized
end
end
def update_ideas
uuid = params[:uuid]
text = params[:text]
title = params[:title]
owner_uuid = params[:owner_uuid]
tag_id_1 = params[:tag_id_1]
tag_id_2 = params[:tag_id_2]
tag_id_3 = params[:tag_id_3]
tag_id_4 = params[:tag_id_4]
updated_at = params[:timeStamp]
#idea = Idea.new(:uuid => uuid, :text => text, :title => title, :owner_uuid => owner_uuid, :tag_ids => [tag_id_1, tag_id_2, tag_id_3, tag_id_4], :updated_at => updated_at)
#idea.save!
render json: {:status => 200}
end
def getjson
token = params[:token]
#user = User.find_by_token(token)
#users = User.all
#owner = Owner.find_by_user_uuid(#user.uuid)
#Owners = Owner.all
ownerUuid = #owner.uuid
#tags=Tag.all
#ideas=Idea.where(:owner_uuid => ownerUuid)
#votes=Vote.all
#votings=Voting.all
end
# def token_auth
# token = params[:token]
# #owner = Owner.find_by_token(token)
# if #owner
# update_ideas
# end
# end
end
the error happens in method "update_ideas" the following line
#idea = Idea.new(:uuid => uuid, :text => te...
Idea Model:
class Idea < ActiveRecord::Base
self.primary_key = :uuid
has_many :idea_tags
has_many :tags, through: :idea_tags
belongs_to :voting
has_many :votes
belongs_to :owner
end
Idea migration file
class CreateIdeas < ActiveRecord::Migration
def change
create_table :ideas, :id => false, :primary_key => :uuid do |i|
i.string :uuid
i.string :title
i.string :text
i.string :owner_uuid
i.string :voting_uuid
i.datetime :created_at
i.datetime :updated_at
end
end
end
How do i save Objects like this proper?
Since you are using uuid as primary key for ideas, I'm guessing you have idea_uuid field in IdeaTag? If yes, you need to add foreign_key: 'idea_uuid to has_many :idea_tags, otherwise it will by default assume foreign_key is idea_id . You might have to add it to belongs_to methods as well.
has_many :idea_tags, foreign_key: 'idea_uuid'
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Ruby on Rails, View properties

I get this error and for the life of me I can't figure out why. Help would be appreciated. :
error 3: error displayed after changes
error 4: after User.all.each do |user|
Error: Undefined method 'each' for nil: nilClass
my ruby/haml code is as follows
viewer code:
-# This file is app/views/projects/index.html.haml
%h1 All Project Tasks
= form_tag projects_path, :method => :get do
Include:
- #all_users.each do |user|
= user
= check_box_tag "users[#{user}]", 1, ("checked" if #filtered_users.find_index(user))
= submit_tag 'Refresh', :id => "users_submit"
%table#projects
%thead
%tr
%th{:class => ("hilite" if params[:sort] == "title")}= link_to "Title", {:controller => "projects", :sort => "title", :filter => #filtered_users.to_s}, :id => "title_header"
%th Task Details
%th Assigned Usertimot
%th{:class => ("hilite" if params[:sort] == "due_date")}= link_to "Due Date", {:controller => "projects", :sort => "due_date", :filter => #filtered_users.to_s}, :id => "due_date_header"
%tbody
- #projects.each do |project|
%tr
%td= project.title
%td= link_to "More about #{project.title}", project_path(project)
%td= project.user
%td= project.due_date.to_formatted_s(:long)
= link_to 'Add new project task', new_project_path
controller code:
class ProjectsController < ApplicationController
def show
id = params[:id] # retrieve project task ID from URI route
#project = Project.find(id) # look up project task by unique ID
# will render app/views/projects/show.<extension> by default
def index
#projects_users = Project.all_users
# remembered settings
if (params[:filter] == nil and params[:users] == nil and params[:sort] == nil and
(session[:filter] != nil or session[:users] != nil or session[:sort] != nil))
if (params[:filter] == nil and session[:filter] != nil)
params[:filter] = session[:filter]
end
if (params[:sort] == nil and session[:sort] != nil)
params[:sort] = session[:sort]
end
redirect_to projects_path(:filter => params[:filter], :sort => params[:sort], :users => params[:users])
else
if (params[:filter] != nil and params[:filter] != "[]")
#filtered_users = params[:filter].scan(/[\w-]+/)
session[:filter] = params[:filter]
else
#filtered_users = params[:users] ? params[:users].keys : []
session[:filter] = params[:users] ? params[:users].keys.to_s : nil
end
end
session[:sort] = params[:sort]
session[:users] = params[:users]
if (params[:sort] == "title")
if ( params[:users]or params[:filter] )
#projects = Project.find(:all, :order => "title")
end
end
if (params[:sort] == "due_date")
if ( params[:users]or params[:filter] )
#projects = Project.find(:all, :order => "due_date")
end
if (params[:sort] == nill)
if(params[:users] or params[:filter])
#projects = Project.all
end
end
end
end
def new
# default: render 'new' template
end
def create
#project = Project.create!(project_params)
flash[:notice] = "#{#project.title} was successfully created."
redirect_to projects_path
end
def edit
#project = Project.find params[:id]
end
def update
#project = Project.find params[:id]
#project.update_attributes!(project_params)
flash[:notice] = "#{#project.title} was successfully updated."
redirect_to project_path(#project)
end
def destroy
#project = Project.find(params[:id])
#project.destroy
flash[:notice] = "Project '#{#project.title}' deleted."
redirect_to projects_path
end
private
def project_params
params.require(:project).permit(:title, :description, :extended_description, :user, :due_date)
end
end
end
i understand that the spacing for haml may be a little off, just the nature of trying to format the code block thanks in advance!
viewer code:
class Project < ActiveRecord::Base
def self.all_users
allUsers = []
Project.all.each do |project|
if (allUsers.find_index(project.user) == nil)
allUsers.push(project.user)
end
end
return allUsers
end
end
You are probably getting the error on this line in your view:
#all_users.each do |user|
The reason for the error as I see it is that you don't have #all_users instantiated anywhere in your controller's index action method.
First switch #all_users to #projects_users. Also it appears that your all_users method in project.rb is overly complex and is returning nil. Try modifying project.rb to the following:
class Project < ActiveRecord::Base
def self.all_users
all.includes(:user).map(&:user).uniq
end
end
Undefined method 'each' for nil: nilClass
This error basically means you don't have any data in your variable.
In other languages, it would mean you've not delcared the variable. Because Ruby is object orientated, it will populate the variable with the nilClass class.
Many new Ruby devs are thrown by the "undefined method" exception message; it's the nilClass you have to look out for.
--
To explain the error properly, because Ruby is object orientated, every variable is actually a data object, represented by a class. In Rails, you can define these classes as models (User.find etc).
Unlike other languages, Ruby treats these objects as is -- it uses methods on them. Other languages fit data into functions, E.G PHP's each function:
#PHP
<$ each($people) $>
#Ruby
<% #people.each do |person| %>
Thus, the "no method" error basically means that Ruby cannot find the method you're calling on the nilClass. It throws developers because they think that "I have the x method on the User class", not realizing that the variable has been populated by the nilClass instead.
The short of it is that you have to either make your calls conditional, or populate the variable properly.
The error appears to be here:
#app/views/project/index.html.haml
#all_users.each do |user|
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def index
#projects_users = Project.all_users
end
end
You're not assigning #all_users at all
You're using an inefficient way to get "all users"
Here's what I'd do:
#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
def index
#users = Project.all_users
end
end
#app/models/project.rb
class Project < ActiveRecord::Base
scope :all_users, -> { joins(:users) } #-> this needs to be tested
end
#app/views/projects/index.haml
- #users.each do |user|
= user.name
I am pretty inexperienced with pure SQL, you'll be best referring to the joins documentation for a clearer perspective.

Define several json formats for model

In my rails app i defined a specific JSON-Format in my model:
def as_json(options={})
{
:id => self.id,
:name => self.name + ", " + self.forname
}
end
And in the controller i simply call:
format.json { render json: #patients}
So now im trying to define another JSON-Format for a different action but i dont know how?
How do i have to define another as_json or how can i pass variables to as_json? Thanks
A very ugly method but you can refactor it for better readability:
def as_json(options={})
if options.empty?
{ :id => self.id, :name => self.name + ", " + self.forname }
else
if options[:include_contact_name].present?
return { id: self.id, contact_name: self.contact.name }
end
end
end
Okay, I should give you a better piece of code, here it is:
def as_json(options = {})
if options.empty?
self.default_json
else
json = self.default_json
json.merge!({ contact: { name: contact.name } }) if options[:include_contact].present?
json.merge!({ admin: self.is_admin? }) if options[:display_if_admin].present?
json.merge!({ name: self.name, forname: self.forname }) if options[:split_name].present?
# etc etc etc.
return json
end
end
def default_json
{ :id => self.id, :name => "#{self.name}, #{self.forname}" }
end
Usage:
format.json { render json: #patients.as_json(include_contact: true) }
By defining hash structure by 'as_json' method, in respective model class i.e User model in (Example 1), it becomes the default hash stucture for active record(i.e., user) in json format. It cannot be overridden by any inline definitions as defined in Example: 2
Example 1:
class User < ActiveRecord::Base
.....
def as_json(options={})
super(only: [:id, :name, :email])
end
end
Example: 2
class UserController < ApplicationController
....
def create
user = User.new(params[:user])
user.save
render json: user.as_json( only: [:id, :name] )
end
end
Therefore, in this example when create action is executed 'user' is returned in ("only: [:id, :name, :email]") format not as ("only: [:id, :name]")
So, options = {} are passed to as_json method to specifiy different format for different methods.
Best Practice, is to define hash structure as constant and call it everwhere it is needed
For Example
Ex: models/user.rb
Here, constant is defined in model class
class User < ActiveRecord::Base
...
...
DEFAULT_USER_FORMAT = { only: [:id, :name, :email] }
CUSTOM_USER_FORMAT = { only: [:id, :name] }
end
Ex: controllers/user.rb
class UserController < ApplicationController
...
def create
...
render json: user.as_json(User::DEFAULT_USER_FORMAT)
end
def edit
...
render json: user.as_json(User::CUSTOM_USER_FORMAT)
end
end
Cool!

Rails ActiveRecord relation to JSON

I'm using Rails to query data and put it into a hash like so...
class AssignmentsController < ApplicationController
respond_to :json
def index
student = Student.find(current_user.student_id)
#assignments = Hash.new
#assignments["individual"] = Assignment.where(:student_id => student.id)
unless student.group_lesson_ids.nil?
student.group_lesson_ids.each do |g|
group_lesson = GroupLesson.find(g)
#assignments[group_lesson.name] = Assignment.where(:group_lesson_id => g)
end
end
end
end
Then I want Rabl to turn this into JSON to be used by a Marionette app.
Here's the Rabl file
object #assignments
attributes :id, :title, :student_id, :assigned
But when I inspect the JSON in the browser, it just shows me the ActiveRecord relation.
{
"#<ActiveRecord::Relation::ActiveRecord_Relation_Assignment:0x007fa2956b43a8>": [
{ },
{ },
{ }
]
}
I understand this is because of the concept of lazy loading, but what should I do in this situation to make the JSON available to Marionette?
How about this, provided you have relationships between models specified (not tested since I'm not currently using RABL):
class AssignmentsController < ApplicationController
respond_to :json
def index
#student = current_user.student
end
end
RABL template:
object false
node :assignments do
child #student.assignments => :individual
#student.group_lessons.each do |gl|
node(gl.name) { gl.assignments }
end
end

Resources