Building a Rails 3.2 app with Ruby 1.9.
I am trying to write a helper method that initializes 3 variables and when i try to call the initialized variables from my view i get an "undefined method" error.
Method In Helper File
module StoreHelper
class Status
def initialize(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
#avoid nil class errors for vol2 and 3. volume 1 can never be nil
if product.volume2.nil?
product.volume2 = 0
end
if product.volume3.nil?
product.volume3 = 0
end
#Promo status logic
if (product_sales >= product.volume2) && (product_sales < product.volume3)
#level3_status = "Active"
#level2_status = "On!"
#level1_status = "On!"
elsif (product_sales >= product.volume3)
#level3_status = "On!"
#level2_status = "On!"
#level1_status = "On!"
else #level3_status = "Pending"
end
end
I then attempt to call the the initialized variable #level3_status like so
<%=level3_status (product)%>
Not sure what i'm doing wrong any help would be appreciated.
How long are you programming with ruby? You have to create a new instance of your class to access the instance outside. Take a look at these basics: http://www.tutorialspoint.com/ruby/ruby_variables.htm
UPDATE
From the link above..
Ruby Instance Variables:
Instance variables begin with #. Uninitialized instance variables have the value nil and produce warnings with the -w option.
Here is an example showing usage of Instance Variables.
class Customer
def initialize(id, name, addr)
#cust_id=id
#cust_name=name
#cust_addr=addr
end
def display_details()
puts "Customer id ##cust_id"
puts "Customer name ##cust_name"
puts "Customer address ##cust_addr"
end
end
# Create Objects
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
# Call Methods
cust1.display_details()
cust2.display_details()
That´s how you can work with ruby and instance variables. More details are in the link.
In your case I think you have another "error", you mixed a few things.. where is your helper class? Under the app/helpers/store_helper.rb? In this file you should just add view helpers. If I am right with my intuitions I would solve your problem like following:
app/helpers/store_helper.rb
module StoreHelper
def get_level_states(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
product.volume2 = 0 if product.volume2.nil?
product.volume3 = 0 if product.volume3.nil?
levels = {}
if (product_sales >= product.volume2) && (product_sales < product.volume3)
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "Active!"
elsif product_sales >= product.volume3
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "On!"
else
levels[:3] = "Pending"
end
levels
end
end
app/views/your_views_folder/your_view.html.erb
to get the different level state:
<% levels = get_level_states(product) %>
<%= levels[:1] %> # will print the level 1
<%= levels[:2] %> # will print the level 2
<%= levels[:3] %> # will print the level 3
Related
I have refined the Enumerable module in a Rails controller as follows, adding one method that, as far as I know, doesn't already exist in Rails or the standard library:
module OrderedGroupBy
refine Enumerable do
def ordered_group_by(&block)
items = each
groups = []
prev = nil
loop do
begin
i = items.next
current = i.instance_eval(&block)
groups << [] if current != prev
prev = current
groups.last << i
rescue StopIteration
break
end
end
groups
end
end
end
using OrderedGroupBy
class TestController < ApplicationController
def test
numbers = ['one', 'three', 'seven', 'four', 'nine']
#groups = numbers.ordered_group_by(&:length)
end
end
Calling the controller action test results in a NoMethodError:
undefined method `ordered_group_by' for ["one", "three", "seven", "four", "nine"]:Array
To narrow down the problem, I created a standalone file with the same structure (without the Rails environment):
module OrderedGroupBy
refine Enumerable do
def ordered_group_by(&block)
items = each
groups = []
prev = nil
loop do
begin
i = items.next
current = i.instance_eval(&block)
groups << [] if current != prev
prev = current
groups.last << i
rescue StopIteration
break
end
end
groups
end
end
end
using OrderedGroupBy
class TestController < Object
def test
numbers = ['one', 'three', 'seven', 'four', 'nine']
#groups = numbers.ordered_group_by(&:length)
end
end
TestController.new.test
In this case, calling the test method has the expected result:
# => [["one"], ["three", "seven"], ["four", "nine"]]
Also, if I define the ordered_group_by method directly on Enumerable, without using refine, I can successfully call the method.
Is there some reason why this would not work as a refinement in the context of Rails, or is there something else I'm missing?
Edit: I'm using rails 5.2.0, and this is an empty project, with only rails new _ and rails generate controller Test test having been run.
I have created Rails application and I have used lots of instance variables and most of them are not required in the views. Do I need to replace the unused instance variables for improving the performance?
Sample code:
def show
custom_fields_data = fetch_custom_field_data
#selected_custom_fields_opt_from_view = []
if custom_fields_data.present?
#listings = #listing.category.listings.where("price_cents!=? AND open= ?",0,true).reject { |l| l.author.main_admin? }
#selected_custom_fields_opt_from_view = custom_fields_data.map do |custom_field_data|
CustomField.find(custom_field_data[0]).options.find(custom_field_data[1])
end
#listings.each do |listing|
# array to store the selected a custom field's option from Database
selected_custom_fields_opt_from_db = []
listing.custom_field_values.each do |custom_field_value|
selected_custom_fields_opt_from_db.push(custom_field_value.selected_options.first)
end
if selected_custom_fields_opt_from_db.uniq.sort == #selected_custom_fields_opt_from_view.uniq.sort || (#selected_custom_fields_opt_from_view - selected_custom_fields_opt_from_db).empty?
similar_listing.push(listing)
end
end
#listings = similar_listing
end
#listing_with_filters = similar_listing.present? ? #listings.first : #listing
#selected_tribe_navi_tab = "home"
unless current_user?(#listing.author)
#listing.increment!(:times_viewed)
end
#current_image = if params[:image]
#listing.image_by_id(params[:image])
else
#listing.listing_images.first
end
#prev_image_id, #next_image_id = if #current_image
#listing.prev_and_next_image_ids_by_id(#current_image.id)
else
[nil, nil]
end
payment_gateway = MarketplaceService::Community::Query.payment_type(#current_community.id)
process = get_transaction_process(community_id: #current_community.id, transaction_process_id: #listing.transaction_process_id)
form_path = new_transaction_path(listing_id: #listing.id)
delivery_opts = delivery_config(#listing.require_shipping_address, #listing.pickup_enabled, #listing.shipping_price, #listing.shipping_price_additional, #listing.currency)
#category = #listing.category
#template_listing = #category.template_listing
if #current_user
# For Pivot table
#selected_custom_field = params[:custom_field] if params[:custom_field]
#listing_for_pivot = Listing.new
#listing_images = #listing.listing_images
#shape = get_shape(#listing.listing_shape_id)
#unit_options = ListingViewUtils.unit_options(#shape[:units], unit_from_listing(#template_listing)).first if #shape
#custom_field_questions = #category.custom_fields
#numeric_field_ids = numeric_field_ids(#custom_field_questions)
#category_tree = CategoryViewUtils.category_tree(
categories: ListingService::API::Api.categories.get(community_id: #current_community.id)[:data],
shapes: get_shapes,
locale: I18n.locale,
all_locales: #current_community.locales
)
if #template_listing.present?
#listing_for_pivot.title = #template_listing.title
#listing_for_pivot.description = #template_listing.description
#listing_images = #template_listing.listing_images if #template_listing.listing_images.present?
#listing_for_pivot.listing_shape_id = #template_listing.listing_shape_id
end
if (#current_user.location != nil)
temp = #current_user.location
temp.location_type = "origin_loc"
#listing_for_pivot.build_origin_loc(temp.attributes)
else
#listing_for_pivot.build_origin_loc(:location_type => "origin_loc")
end
#custom_field_area = CategoryCustomField.where(category_id: #category.id, custom_field_id: #category.custom_fields.pluck(:id))
#row = #category.custom_field_row
#row = #custom_field_area.first.custom_field if #row.nil? && #custom_field_area.first
#column = #category.custom_field_column
#column = #custom_field_area.second.custom_field if #column.nil? && #custom_field_area.second
#filters = #category.custom_field_filters
#filters = #custom_field_area.all.from(1).map { |category_custom_field| category_custom_field.custom_field } if #filters.nil? && #custom_field_area.size > 2
#selected_value_for_filter = []
if #filters.present?
if #selected_custom_field
#filters.each do |filter|
if (#selected_custom_field["#{filter.id.to_s}_"])
#selected_value_for_filter.push(filter.options.find(#selected_custom_field["#{filter.id.to_s}_"]))
else
#selected_value_for_filter.push(filter.options.first)
end
end
else
#filters.each do |filter|
#selected_value_for_filter.push(filter.options.first)
end
end
end
# Pivot table section end
end
#applicant = #category.listings.pluck(:author_id).uniq
#suggested_business_accounts = #category.people.where("people.id NOT IN (?)", #applicant);
if #suggested_business_accounts.present?
#business_locations =
#suggested_business_accounts.map do |person|
person.location
end
#business_locations.compact!
end
render locals: {
form_path: form_path,
payment_gateway: payment_gateway,
# TODO I guess we should not need to know the process in order to show the listing
process: process,
delivery_opts: delivery_opts,
listing_unit_type: #listing.unit_type
}
end
It is not recommended to use instance variables if you don't want to send them to views. The scope of the variables should be narrowest, therefore in your case if you are not using instance variables in the views you should convert them to local.
Using instance variables instead of local variables is a bad idea at least memory-wise.
Instance variable exists while the object that holds it exists. On the contrary, local variable exists only inside method/block it is defined.
Garbage collector does not care whether you use instance variable elsewhere beyond the method or not.
Thus, if you have instance variables, which you only intend to use within the method - change them to local ones.
I am working on a weather app using the Weather Underground API. Everything almost appears to be working fine in my web app. Except for one thing. When ever I start up my server and head to my index page, in this case, the main page. I get the following error message: undefined method []' for nil:NilClass. Checking my logs, I see the following message: NoMethodError (undefined method '[]' for nil:NilClass):app/controllers/welcome_controller.rb:14 in index.
now my controller looks like this:
class WelcomeController < ApplicationController
def index
#states will need to be defined and then #states.sort will sort all of them on the form.
#states = %w(HI AK CA OR WA ID UT NV AZ NM CO WY MT ND SD NE KS OK TX LA AR
MO IA MN WI IL IN MI OH KY TN MS AL GA FL SC NC VA WV DE MD PA NY NJ CT RI
MA VT NH ME DC PR)
#states.sort!
#Here is the call to the API
response = HTTParty.get("http://api.wunderground.com/api/#
{ENV['wunderground_api_key']}/geolookup/conditions/q/#{params[:state]}/#
{params[:city]}.json")
#location = response['location']['city']
#temp_f = response['current_observation']['temp_f']
#temp_c = response['current_observation']['temp_c']
#weather_icon = response['current_observation']['icon_url']
#weather_words = response['current_observation']['weather']
#forecast_link = response['current_observation']['forecast_url']
#real_feel = response['current_observation']['feelslike_f']
#This part of the code will change the background depending on what
#weather_words is.
#Head over to the views/layouts/application.html.erb file to see more.
if #weather_words == "Partly Cloudy" || #weather_words == "Mostly Cloudy"
#body_class = "partly-cloudy"
elsif #weather_words == "Cloudy" || #weather_words == "Scattered Clouds" || #weather_words == "Overcast"
#body_class = "partly-cloudy"
elsif #weather_words == "Clear"
#body_class = "sunny"
elsif #weather_words == "snow"
#body_class = "snow"
elsif #weather_words == "Rain"
#body_class = "rain"
elsif #weather_words == "Fog"
#body_class = "fog"
elsif #weather_words == "Thunderstorms and Rain" || #weather_words == "Thunderstorms"
#body_class = "thunder"
end
end
Now, I have tracked down the problem, I believe, to the params :state and :city not being filled in when I load the page. If I delete this part of the code:
#location = response['location']['city']
#temp_f = response['current_observation']['temp_f']
#temp_c = response['current_observation']['temp_c']
#weather_icon = response['current_observation']['icon_url']
#weather_words = response['current_observation']['weather']
#forecast_link = response['current_observation']['forecast_url']
#real_feel = response['current_observation']['feelslike_f']
Then load the page, everything will work fine if I select a state and city, then add the above deleted code-it will pull it up. Except I cannot start my server and go directly to my index page or else it will crash. I also tried placing the following in:
params[:state] = "MA"
params[:city] = "Boston"
and that will load the page just fine except I am stuck on Boston! Finally, Here are my routes:
#The index page gets two routes:
#The get route for when we initially come to the page
get 'index' => 'welcome#index'
#And then a post route for when we come back to the index page after
# submitting the form
post 'index' => 'welcome#index'
Any help will be great! I also have all of my code posted at github, username is ravenusmc. Again, thank you for the help.
One or more fields of response are probably nil. This is a very common mistake; you should always check if the variable is nil or empty before trying to access nested hash keys or array positions. e.g., insted of #location = response['location']['city'], use something like:
#location = response['location'] ? response['location']['city'] : nil
Do the same for the rest of the #location = response... attributions.
If you're using ruby 2.3.0, you can use the dig method:
#location = response.dig('location', 'city')
Which handles nil values for you. See the difference:
2.3.0 :004 > response = {}
# => {}
2.3.0 :005 > response.dig('location', 'city')
# => nil
2.3.0 :006 > response['location']['city']
NoMethodError: undefined method `[]' for nil:NilClass
from (irb):6
from /home/lbrito/.rvm/rubies/ruby-2.3.0/bin/irb:11:in `<main>'
I am a complete beginner to Ruby. I am working on Lesson 45 of Learn Ruby the Hard Way currently and am creating a game similar to Zork and Adventure.
I have created a structure where I am creating 'scenes' in different files and requiring all the scenes in one file where I have an engine/map that ensures if the current scene does not equal 'finished' that it runs 'X' scene's 'enter' method.
However I have two issues:
1) I keep getting a error saying 'Warning class variable access from top level'
2) Even though the script is running I get
ex45.rb:30:in `play': undefined method `enter' for nil:NilClass (NoMethodError) from ex45.rb:59:in
The following is all of my code from each file. My apologies if it's a long read, but I would love to know why I am getting these two errors and what I can do to fix them.
Ex45.rb:
require "./scene_one.rb"
require "./scene_two.rb"
require "./scene_three.rb"
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
current_scene.enter()
end
end
class Map
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
a_map = Map.new('scene_one')
a_game = Engine.new(a_map)
a_game.play()
scene_one.rb:
class SceneOne
def enter
puts "What is 1 + 2?"
print "> "
answer = $stdin.gets.chomp
if answer == "3"
puts "Good job"
return 'scene_two'
else
puts "try again"
test
end
end
end
scene_two.rb
class SceneTwo
def enter
puts "1 + 3?"
print "> "
action = $stdin.gets.chomp
if action == "4"
return 'scene_three'
else
puts "CANNOT COMPUTE"
end
end
end
scene_three.rb
class SceneThree
def enter
puts "This is scene three"
end
end
Thanks in advance!
Answer to your first question:
You need to move the class variable definitions inside your Map class to get rid of these warnings:
Ex45.rb:5: warning: class variable access from toplevel
Ex45.rb:6: warning: class variable access from toplevel
Ex45.rb:7: warning: class variable access from toplevel
So, your Map class would look like this:
class Map
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
To answer your 2nd question:
You are getting undefined method 'enter' for nil:NilClass (NoMethodError) because your current_scene becomes nil at some point and then you try to call: current_scene.enter() i.e. nil.enter and it fails with that error message.
To solve this problem, you have to make sure you always have some value in your current_scene i.e. make sure it's not nil.
I think, you can just remove current_scene.enter() line from the end of your play method in the Engine class. So, your Engine class will look like this:
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
# current_scene.enter()
end
end
And, you won't get that error anymore.
Just so you know:
##y = 20
p Object.class_variables
--output:--
1.rb:1: warning: class variable access from toplevel
[:##y]
And:
class Object
def self.y
##y
end
end
puts Object.y
--output:--
20
But:
class Dog
##y = "hello"
def self.y
##y
end
end
puts Dog.y #=>hello
puts Object.y #=>What do you think?
The output of the last line is the reason that class variables are not used in ruby. Instead of class variables, you should use what are known as class instance variables:
class Object
#y = 10 #class instance variable
def self.y
#y
end
end
puts Object.y
class Dog
#y = "hello"
def self.y
#y
end
end
puts Dog.y #=> hello
puts Object.y #=> 10
A class instance variable is just an #variable that is inside the class, but outside any def. And instead of there being one ##variable that is shared by all the subclasses, each subclass will have its own #variable.
Working on final class project. I need to calculate the GPA of my major credits and non major credits separately for a transcript page. When I run this code below as a controller it works fine and show my total credit hours for major and non major but when I put this code
#GPA_for_major = (course.credits * course.grade.scale) / course.credits
in the If statement I get NoMethodError in TransController#transcript
undefined method 'credits' for # Course::ActiveRecord_Relation:0x00000007b99798>
class Transcript
def initialize (course_array)
#course = course_array
#total_non_major_credits = 0
#total_major_credits = 0
#GPA_for_major = 0
#GPA_for_non_major = 0
for item in #course
if item.is_for_major
#total_major_credits = #total_major_credits + item.credits
else
#total_non_major_credits = #total_non_major_credits + item.credits
end
end
end
def course
#course
end
def total_non_major_credits
#total_non_major_credits
end
def total_major_credits
#total_major_credits
end
def GPA_for_major
#GPA_for_major
end
def GPA_for_non_major
#GPA_for_non_major
end
end
This is the Controller for my transcript page
class TransController < ApplicationController
def transcript
#courses = Course.all
#transcript =Transcript.new(#courses)
end
end
I'm not sure what else to include because this is my first post but any help will be awesome! Thanks!
#course appears to refer to an array of courses and the if statement is within a loop that iterates over the items setting a local variable item for each course. Given that, you should use item instead of course:
#GPA_for_major = (item.credits * item.grade.scale) / item.credits