Ruby beginner struggling to simply print out the value of this ##people hash to the console
class Person
#have a first_name and last_name attribute with public accessors
attr_accessor :first_name
attr_accessor :last_name
#have a class attribute called `people` that holds an array of objects
##people = []
#have an `initialize` method to initialize each instance
def initialize( first_name, last_name )#should take 2 parameters for first_name and last_name
#assign those parameters to instance variables
#first_name = first_name
#last_name = last_name
#add the created instance (self) to people class variable
##people.push self
end
#have a `search` method to locate all people with a matching `last_name`
def self.search( last_name )
#accept a `last_name` parameter
#search_name = last_name
#search the `people` class attribute for instances with the same `last_name`
##people.select {|last_name, value| value == "Smith"}.to_s
#return a collection of matching instances
end
#have a `to_s` method to return a formatted string of the person's name
def to_s
#return a formatted string as `first_name(space)last_name`
self.each { |first_name,last_name| print "#{first_name} #{last_name}" }
end
def print_hash
p ##people
end
end
p1 = Person.new("John", "Smith")
p2 = Person.new("John", "Doe")
p3 = Person.new("Jane", "Smith")
p4 = Person.new("Cool", "Dude")
#puts Person.search("Smith")
puts Person.print_hash
# Should print out
# => John Smith
# => Jane Smith
You defined print_hash as an instance method. To be able to call it like People.print_hash define it this way: def self.print_hash
Related
How to shortcut or compact nested clauses like this?
if #user.name.present?
if #user.username.present?
name = #user.name + #user.username
else
name = #user.name
end
else
name = ""
end
I thought some way to concatenate both strings (like a = a + b)
name= #user.try(:name) ? "#{#user.name}#{#user.try(:username)}" : ""
If you want to concat irrespective of absence of any of the attribute you could try below one.
name= "#{#user.try(:name)} #{#user.try(:username)}"
The try method returns value if present or nil value. Moreover you can nest deep with try without checking for its presence.
In ruby , use Array to do it.
[#user.name, #user.username].compact.join(" ")
here is the example
class User
def name
"john"
end
def username
"doe"
end
end
class NoUserName
def name
"john"
end
def username
""
end
end
class NilUserName
def name
"john"
end
def username
nil
end
end
class NilUser
def name
nil
end
def username
"Doe"
end
end
class NoUser
def name
""
end
def username
"Doe"
end
end
#user= User.new
puts [#user.name, #user.username].compact.join(" ").squeeze(' ').strip # "John Doe"
#user= NoUserName.new
puts [#user.name, #user.username].compact.join(" ").squeeze(' ').strip # "John"
#user= NilUserName.new
puts [#user.name, #user.username].compact.join(" ").squeeze(' ').strip # "John"
#user= NoUser.new
puts [#user.name, #user.username].compact.join(" ").squeeze(' ').strip # "Doe"
#user= NilUser.new
puts [#user.name, #user.username].compact.join(" ").squeeze(' ').strip # "Doe"
I am querying my ActiveRecords in rails with the following:
result = MyObj.where({customer: current_id}).as_json()
There are two columns returned:
result = [{id:1, name: "david", last_name: "Smith:"}]
I would like create a third column (which will not be saved to the DB) like so:
result = [{id:1, name: "David", last_name: "Smith:", full_name:"David Smith"}]
Is this possible within the WHERE query?
Add a full_name method to your MyObj model, then pass methods: :full_name to the as_json method:
class MyObj
def full_name
"{name} #{last_name}"
end
end
result = MyObj.where({customer: current_id}).as_json(methods: :full_name)
From the documentation for as_json:
To include the result of some method calls on the model use :methods:
user.as_json(methods: :permalink)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "permalink" => "1-konata-izumi" }
Or alternately, you could override as_json on the model to include full_name by default:
class MyObj
def full_name
"{name} #{last_name}"
end
def as_json(options={})
super({methods: :full_name}.merge options)
end
end
Sure. Override the method in your model...
class MyObj < ActiveRecord::Base
def full_name
"#{name} #{last_name}"
end
def as_json options={}
{
id: id,
name: name,
last_name: last_name,
full_name: full_name
}
end
end
Quick and dirty just manipulate the results you get back
result = MyObj.where({customer: current_id})
result.map{|customer| "full_name: #{customer.first_name + customer.last_name}" }
But be careful of nil values.
In my Rails API, I'd like a Mongo object to return as a JSON string with the Mongo UID as an "id" property rather than as an "_id" object.
I want my API to return the following JSON:
{
"id": "536268a06d2d7019ba000000",
"created_at": null,
}
Instead of:
{
"_id": {
"$oid": "536268a06d2d7019ba000000"
},
"created_at": null,
}
My model code is:
class Profile
include Mongoid::Document
field :name, type: String
def to_json(options={})
#what to do here?
# options[:except] ||= :_id #%w(_id)
super(options)
end
end
You can monkey patch Moped::BSON::ObjectId:
module Moped
module BSON
class ObjectId
def to_json(*)
to_s.to_json
end
def as_json(*)
to_s.as_json
end
end
end
end
to take care of the $oid stuff and then Mongoid::Document to convert _id to id:
module Mongoid
module Document
def serializable_hash(options = nil)
h = super(options)
h['id'] = h.delete('_id') if(h.has_key?('_id'))
h
end
end
end
That will make all of your Mongoid objects behave sensibly.
For guys using Mongoid 4+ use this,
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end
Reference
You can change the data in as_json method, while data is a hash:
class Profile
include Mongoid::Document
field :name, type: String
def as_json(*args)
res = super
res["id"] = res.delete("_id").to_s
res
end
end
p = Profile.new
p.to_json
result:
{
"id": "536268a06d2d7019ba000000",
...
}
Use for example:
user = collection.find_one(...)
user['_id'] = user['_id'].to_s
user.to_json
this return
{
"_id": "54ed1e9896188813b0000001"
}
class Profile
include Mongoid::Document
field :name, type: String
def to_json
as_json(except: :_id).merge(id: id.to_s).to_json
end
end
If you don't want to change default behavior of MongoId, just convert result of as_json.
profile.as_json.map{|k,v| [k, v.is_a?(BSON::ObjectId) ? v.to_s : v]}.to_h
Also, this convert other BSON::ObjectId like user_id.
# config/initializers/mongoid.rb
# convert object key "_id" to "id" and remove "_id" from displayed attributes on mongoid documents when represented as JSON
module Mongoid
module Document
def as_json(options={})
attrs = super(options)
id = {id: attrs["_id"].to_s}
attrs.delete("_id")
id.merge(attrs)
end
end
end
# converts object ids from BSON type object id to plain old string
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end
I have a method within my main model, which should return specific values based on the set params:
def self.retrieve_profiles( params )
case params[:view]
when 'image_only'
fields = %w{ avatar }
when 'profile_minimal'
fields = %w{ avatar username age }
when 'profile_medium'
fields = %w{ avatar username age first_name last_name bio relationship_status seeking }
when 'profile_extended'
fields = %w{ avatar username age first_name last_name bio relationship_status seeking country city photos }
end
profiles = Profile.that_has_photos
profiles = profiles.where( :country_id => params['country'] ) unless params['country'].nil?
profiles = profiles.order( "RAND()" ).limit( params['count'] )
# works fine up to here (returns all fields)
profiles.each do |a|
fields.each do |field|
puts a.eval(field)
end
end
# above block makes it fail (private method `eval' called)
end
My question is: how do I return only the values specified by the fields hash ?
Use send instead of eval. This code should work fine.
profiles.each do |a|
fields.each do |field|
puts a.send(field)
end
end
I want to be able to initialize a new Car object and pass it a Person object in the parameters, so it can be saved in that Person's #cars array. Currently, I take this approach:
person = Person.new("Michael")
car = Car.new("Honda", "Accord")
person.add_car(car)
person.add_car(Car.new("Ford", "Taurus"))
person.add_car(Car.new("Toyota", "Prius"))
person.display
However, I'd like to be able to create a new car instance and pass it the Person object I want it associated with. For example:
person = Person.new("Michael")
Car.new("Honda", "Accord", person)
Car.new("Toyota", "Camry", person)
Car.new("Chevy", "Tahoe", person)
person.display
Is that even possible?
class Person
attr_accessor :name
def initialize(name)
super
#name = name
#cars = []
end
def display
puts "#{#name} has #{#cars.length} cars"
puts "----------------------------"
#cars.each do |car|
puts "#{car.make} #{car.model}"
end
end
def add_car(car)
#cars.push(car)
end
end
class Car
attr_accessor :make, :model
def initialize(make, model)
#model = model
#make = make
end
def display
puts "#{#make} #{#model}"
end
end
Yes, that is possible, Car#initialize can call methods on its arguments:
class Car
def initialize(make, model, person = nil)
#model = model
#make = make
person.add_car(self) if(person)
end
#...
end
This would be my implementation:
class Car
attr_accessor :make, :model
def initialize(make, model)
self.make = make
self.model= model
end
end
Person class
class Person
attr_accessor :name, :cars
def initialize(name, cars=[])
self.name = name
self.cars = cars || []
end
def add_car(*args)
raise ArgumentError, 'invalid arguments' if (
(args.size > 2 or args.size == 0) or
(args.size == 1 and !args[0].is_a?(Car))
)
new_car = (args.size == 2) ? Car.new(*args) : args[0]
self.cars << new_car
new_car
end
end
Now you can:
person = Person.new("Michael")
car = Car.new("Honda", "Accord")
person.add_car(car)
person.add_car("Ford", "Taurus")
person.add_car("Toyota", "Prius")
person.display
The add_car method creates a new car when make and model are passed as parameters.
Yes, it is possible as mu is too short's answer demonstrated, but that doesn't really make sense in my opinion. Your cars can't be used in any context without a Person, and said parameter contributes no data necessary to construct the Car object.
I would design such an API as follows:
class Person
def initialize(name)
#name = name
end
def cars
#cars ||= Array.new # Equivalent to #cars || #cars = []
end
end
person = Person.new 'Michael'
taurus = Car.new 'Ford', 'Taurus'
prius = Car.new 'Toyota', 'Prius'
person.cars << taurus << prius << Car.new('Honda', 'Accord')
This is a simpler and more direct form of KandadaBoggu's implementation that takes advantage of the Array#<< method in order to naturally associate Cars with a Person, and also doubles as an attribute reader.