#Schneems UT on Rails course notes
- Spreadsheet to DB
- Dealing with nil
- Modules
We need a csv file
We need to parse using CSV
require 'csv'
CSV.foreach('path/to/products.csv') do |row|
puts row.inspect
end
=> ["HD Electric Amplifier", "269"]
["Video Audible System", "135"]
["Tag Direct System", "283"]
["Output GPS Kit", "985"]
- This is going to call .foreach on the path of the CSV
- Then for each row we increment through we are going to puts whatever is returned from row.inspect.
Now we need to add to DB with ActiveRecord
CSV.foreach('path/to/products.csv') do |row|
name = row[0]
price = row[1].to_i
Product.create(name: name, price: price)
end
- Here we can use the bracket syntax
- Pull out the first element from the row array and assigning that to a variable of name
- Pull out the second element from the row array and assign it to the variable of price
- Then we run Product.create(:name name, :price price), where the name variable is assigned to the :name key and the price variable is assigned to the :price key.
Put a script inside of a Rake task that we can use inside of a Ruby project, as well as a Rails project
list rake tasks
rake -T
rake about
rake assets:clean
rake assets:precompile
rake db:create
rake db:drop
rake db:fixtures:load
rake db:migrate
rake db:migrate:status
rake db:rollback
rake db:schema:dump
Create your own rake tasks
Lives inside of lib/tasks/import.rake
name it something.rake we are going to start out declaring a namespace This is so we don't have conflicting named rake tasks
namespace :import do
end
Once we've done that, we can then add a task into the rakefile
namespace :import do
tasks :data => :environment do
end
end
Before we run the data tasks, we need to run the environment task Environment is a rake task that rails gives us that loads models, and everything that we would normally be able to use when we have a rails console open
we can also add an optional description to our rake tasks
namespace :import do
desc "imports data from a csv file"
task :data => :environment do
end
end
This is the description that is seen when we run rake -T
namespace :import do
desc "imports data from a csv file"
task :data => :environment do
require 'csv'
CSV.foreach('path/to/products.csv') do |row|
name = row[0]
price = row[1].to_i
Product.create(name: name, price: price)
end
end
end
Now that we have all this setup we need to add some code. We can just take that earlier code and paste it in..
Now we have our rake task built, how do we run it?
we can call $rake import:data This is pretty self explainatory
- import comes from the namespace, :import
- data comes from the task name, :data
Put one off scripts into rake taks (things you just need to run once, but aren't tied into your application all of the time (things you will run yourself))
user = User.where(:name => 'richard').first
puts user.name
=> richard
If someone has removed their account, or a name you are looking for doesn't exist yet, you will be returned a NoMethodError: undefined method 'name' for nil:NilClass
=> NoMethodError: undefined method 'name' for nil:NilClass
nil is our constant enemy - lol we have to be constantly concerned wether we are getting values back from functions
def number_greater_than_twenty_six(number)
if number > 26
"Number is greater than 26"
else
"Nope"
end
end
number_greater_than_twenty_six(88)
=> "Number is greater than 26"
number_greater_than_twenty_six(2)
=> "Nope"
number_greater_than_twenty_six(nil)
=> NoMethodError: undefined method '>' for nil:NilClass
nil has a major weakness we can use to combat it nil behaves like a false in boolean operators
&& or || == Boolean operators
&& == Logical AND
|| == Logical OR
Typically used with if statements We can protect against nil with those
I will be happy if I get burger or fries True = yes you got burger True = yes you got fries
puts true || true
=> true
If you got burgers OR fries, you are going to be happy 1st = yes you got burger 2nd = No you didn't get fries
puts true || false
=> true
If you got burgers OR fries, you are still going to be happy.
One true in a chain of OR will return true
puts false || false || false || true
=> true
nil behaves like false
puts nil || nil || nil || true
=> true
Logical AND
I will be happy if I get burger AND fries 1st = I did/didn't get burger 2nd = I did/didn't get fries
puts true && true
=> true
I am happy because I got burger AND fries
put true && false
=> false
I am NOT happy because I got burger, but DIDN'T get fries One false in a chain of && will always return false
puts true && true && true && false
=> false
AND IS A DEMAND Because of this, Ruby will always return false the first time it runs into a false, when evaluating a series of &&/AND's
puts true && true && true && false
Checks first, returns true, then second, then third, then last returns false, so return now and it returns false Ruby never needs to evaluate the rest of those conditions.
nil behaves likes false - Same as above
def number_greater_than_twenty_six(number)
if number > 26
"Number is greater than 26"
else
"Nope"
end
end
number_greater_than_twenty_six(nil)
=> NoMethodError
puts nil > 26
=> NoMethodError
puts nil && nil > 26
=> nil
Because nil is evaluated as false, it doesn't even make it to the nil > 26 It just returns nil, which is false, which in turn returns else or "Nope".
Logic Check
if true
puts "Hello world"
else
puts "nope"
end
=> "Hello world"
What about BANG true (bang negates, or does the opposite)
if !true
puts "Hello world"
else
puts "nope"
end
=> "nope"
If we do just puts user.name and there is no user with that specific name it will return a NoMethodError
user = User.where(:name => 'richard').first
puts user.name
=> NoMethodError
We can get around this by
user = User.where(:name => 'richard').first
if user
puts user.name
end
=>
We can use that knowledge that a nil value is going to behave like false, to protect our calling of user.name If user exists (if user) puts user.name
We can check it more explicitly by doing
user = User.where(:name => 'richard').first
if !user.nil?
puts user.name
end
=>
If not user.nil. If the user is not nil, puts user.name
What about empty arrays? Sometimes we pull empty arrays from the db
user = User.where(:name => 'richard')
if !users.empty?
# ...
end
Here we have used two different operators, but in rails we can just use the .blank operator
nil.blank?
=> true
[].blank?
=> true
"".blank?
=> true
{}.blank?
=> true
"hello there".blank?
=> false
instead of using .nil? or .empty? we can use .blank? .blank? is part of ActiveSupport
user = User.where(:name => 'richard').first
if !user.blank?
puts user.name
end
=> ???
user = User.where(:name => 'richard')
if !users.blank?
users.each { |u| puts u.name }
end
=> ???
Using !user.blank?(user is not blank => return true or false) is still confusing because it is doing the opposite of seeing if it is blank. There is a more simple method to keep things more foreward.
user = User.where(:name => 'richard')
if users.present?
users.each { |u| puts u.name }
end
ActiveSupport also gives us try
user = User.where(:name => 'richard').first
puts user.try(:name)
=> "Richard"
nil.try(:name)
=> nil
Ruby is flexible with if. Don't need to use end if 'if' is inline
user = User.where(:name => 'richard').first
puts user.name if user.present?
Also, we have the ability to use unless. Unless is the opposite of 'if'
user = User.where(:name => 'richard')
puts user.name unless user.blank?
=> ???
Unless is beautiful, please do not use unless with more than one arguement. Makes way more sense to use 'if'
NONO
user = User.where(:name => 'richard').first
puts user.name unless user.blank? ||
user.name == 'richard' &&
user.movie == 'zoolander'
=> ???
One last logic trick Scenario with find or create
user = User.where(:name => 'richard').first
puts user
=> nil
If the record exists find it, if it doesn't create it!
puts nil || "Hello"
=> "Hello"
nil is falsey, so it continues to look for a truthey because of OR ||
puts "foo" || "hello"
=> "foo"
Result is going to return foo because foo is truthey so it will return immediately
We can use this behavior
user = User.where(:name => 'richard').first
user = user || User.create(:name => 'richard')
puts user
=> #<User id: 2, name: 'richard' >
Find a user where the name is richard, return first record. if a user exists, return immediately if a user does not exist, go to the next statement in our or chain, create a user with the :name => 'richard' puts user that returned immediately, or user that was created
Same thing with different syntax
number = 27
number ||= 28
puts number
=> 27
Number is 27, which is not false or nil, do we check the next statement? No, return immediately. Second statement is 27 or == 28
number = nil
number ||= 28
puts number
=> 28
check the next statement because nil is false number is now equal to 28 because 28 is not nil or false
So if I do
user = User.where(:name => 'richard').first
user = user || User.create(:name => 'richard')
puts user
=> #<User>
SAME THING AS
user = User.where(:name => 'richard').first
user ||= User.create(:name => 'richard')
puts user
=> #<User>
To recap we can deal with nil by
&& trick
if and blank?
if and present?
use the ||= (or equals)
Modules are meant to mix in methods into ruby code
class Dog
def bark
puts 'woof'
end
end
class Puppy < Dog
end
puts Puppy.new.bark
=> 'woof'
What if we wanted to share methods across multiple classes
module AnnoyingAnimal
def sit
puts "*chews on shoes*"
end
end
class Puppy < Dog
include AnnoyingAnimal
end
puts Puppy.new.sit
=> "*chews on shoes*"
Alternately, reuse
class Kitty < Cat
include AnnoyingAnimal
end
puts Kitty.new.sit
=> "*chews on shoes*"
REAL EXAMPLE Enumerable module - Used by array
Modules cannot be instantiated Classes can be instantiated
module AnnoyingAnimal
def sit
puts "*chews on shoes*"
end
end
AnnoyingAnimal.new
=> NoMethodError
Include gives us an instance method(a method on the instance)
module AnnoyingAnimal
def sit
puts "*chews on shoes*"
end
end
class Puppy < Dog
include AnnoyingAnimal
end
puts Puppy.new.sit
=> '*chews on shoes*'
Extend gives us class method (a method on the class)
module AnnoyingAnimal
def sit
puts "*chews on shoes*"
end
end
class Puppy < Dog
extend AnnoyingAnimal
end
puts Puppy.sit
=> '*chews on shoes*'