Getting sub strings using other columns in table - Ruby on Rails - ruby-on-rails

Trying to create a table to generate random data using 'faker', for my username column I wanted to concatenate two sub-strings from two other fields in my table.
For example:
First Name: John, Last Name: Abbott, Username: jabbott
I seed my data like so
Post.create!(
Lastname: Faker::Name.last_name,
Firstname: Faker::Name.first_name,
Username: " first letter of first name" + "lastname"
)
Is there a sub string method that does this on Ruby?

Not a "sub string" method, but you can access specific points in strings with []
Example:
username = "#{first_name[0]}#{last_name}".downcase
or you could monkey patch String like this:
class String
def user_name
first_name, last_name = self.split
"#{first_name[0]}#{last_name}".downcase
end
end
And then generate your string like this
name = Faker::Name.name
create_hash = {
:Firstname => name.split.first,
:Lastname => name.split.last,
:Username => name.user_name,
}
Post.create!(create_hash)

Related

What is the best way to loop through a collection of records and pass back an object to the front end?

I have a controller that returns user reports, and one of the methods sums up the points of said reports, per user. I want to pass back an object of this data to the front end so it can be displayed. Ideally my object would be shaped like this:
data: {
users: {
$user_id: {
name: "Foo Bar",
points: 100
},
$user_id: {
name: "Foo Bar Two",
points: 10
}
}
}
However my current implementation is not building the object like this, and simply adding to one big object.
My code looks like this:
def user_points
hash = {}
User.all.each do |u|
user_points = Report.select("points").where("user_id = ?", u.id).sum("points")
hash.merge!(
user:
{
first_name: u.first_name,
last_name:u.last_name,
time_zone: u.time_zone
}
)
end
render json: { data: hash }
end
and the resulting object only included the last user in one big object
data:
user:
first_name: "Test"
last_name: "Test"
points: 200
time_zone: "Pacific Time (US & Canada)"
You can also achieve the same result by joining both the table and then performing aggregation on joined table.
select users.id, users.name, sum(reports.points) as points from users join reports on users.id = reports.user_id group by users.id;
sql-fiddle
Thank you max for the comment.
def user_points
result = User.join(:reports)
.select(
:first_name,
:last_name,
Report.arel_table[:points].sum.as(:points),
:time_zone
)
.group(:id)
render json: { data: result }
end
Output:
data:
first_name: "Test1"
last_name: "Test1"
points: 100
first_name: "Test2"
last_name: "Test2"
points: 200
first_name: "Test3"
last_name: "Test3"
points: 300
As mentioned by dbugger you need to provide a unique key for each hash entry otherwise merge will just replace an existing value.
For example:
{a: :foo}.merge(b: :bar)
=> {:a=>:foo, :b=>:bar}
and
{a: :foo}.merge(b: :bar).merge(a: :foo_bar)
{:a=>:foo_bar, :b=>:bar}
You might want to consider returning a json array rather than an object with unique property names.
maybe something like this?
def user_points
result = User.all.map do |u|
points = Report.select("points").where("user_id = ?", u.id).sum("points")
{
first_name: u.first_name,
last_name:u.last_name,
time_zone: u.time_zone
points: points
}
end
render json: { data: result }
end

How could we replace the ? from the rails sql query

scope :accessible_by, -> (current_user) { where("loc_primary_email= ? OR loc_backup_email= ? ",current_user.email, current_user.email) }
How could I replace ? with :email so, that I don't need to pass current_user.email twice in query
You can replace it just like that, for the "?":
scope :accessible_by, -> (current_user) { where("loc_primary_email = :email OR loc_backup_email = :email ", email: current_user.email) }
But then the argument for where must contain a hash, where the email key must be present.
You can check the docs for this, using an array of as argument (of course, you can omit the brackets);
Alternatively, you can use named placeholders in the template, and
pass a hash as the second element of the array. The names in the
template are replaced with the corresponding values from the hash.
User.where(["name = :name and email = :email", { name: "Joe", email: "joe#example.com" }])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe#example.com';

How do I access a local variable in Active Record query with SQL

I'm trying some code to find an attribute with a time value that is less than current time.
If I have current_time = Time.now, how do I find it using where such as:
Outage.where("end_time < current_time") # this doesn't work.
There are many ways to do that via placeholder.
Using ? Placeholder
You can use ? as a placeholder in a query condition.
User.where('users.name = ?', 'John')
With multiple placeholders:
User.where('users.name = ? AND users.last_name = ?', 'John', 'Smith')
Using Named Placeholder
User.where('first_name = :first_name', { :first_name => 'John' })
With multiple placeholder:
values = { :first_name => 'John', :last_name => 'Smith'}
conditions = 'first_name = :first_name AND last_name = :last_name'
User.where(conditions , values)
Noted that order does not matters. The following code would work correctly since we have already named those placeholders.
values = { :last_name => 'Smith', :first_name => 'John'}
conditions = 'first_name = :first_name AND last_name = :last_name'
User.where(conditions , values)
References: Using Named Placeholders in Ruby
You can use placeholder - ? and then pass the value, like this:
Outage.where('outages.end_time < ?', current_time)

Get other field if this is included in the array object

Title may sound off but below explains more.
I need to know if an item is included in an array object:
[
{ id: 12345, name: "Bob", email: "bob#builder.com" },
...
{ id: 13456, name: "job", email: "joe#farm.com" }
]
In english: If this email present, give me their id
users = User.all
users.any?{|u| u.email == "bob#builder.com"} # true
That will be true. Now, how to get the id of the user which is "12345"? Note, I will not know the id.
Since you seem to be using ActiveRecord, it would be faster to query the database for the email:
# returns the user id or nil, if not found
User.find_by(email: 'bob#builder.com').pluck(:id)
You can try like this if you have array of hashes:
arr.map do |h|
h[:id] if h[:email] == "bob#builder.com"
end.compact
It will return the value of id in an array of all the hashes which satisfies the condition.

Populating database with faker

I want to populate the database with a number of stores and users, that each user corresponds to one store. The issue with the code below is that I get the error Validation failed: Email has already been taken.
namespace :db do
desc "Fill database with sample data"
task populate: :environment do
make_stores
make_users
end
end
def make_stores
50.times do
name = Faker::Company.name
manager = Faker::Name.name
address = Faker::Address.street_name
Store.create!(name: name,
manager: manager,
address: address)
end
end
def make_users
stores = Store.all(limit: 8)
99.times do |n|
first_name = Faker::Name.first_name
last_name = Faker::Name.last_name
email = "example-#{n+1}#example.org"
password = "password"
stores.each { |store| store.users.create!(first_name: first_name,
last_name: last_name,
email: email,
password: password,
password_confirmation: password) }
end
end
The problem is that you're setting the email variable before calling stores.each, so all 8 stores will get a user with the same email.
Do something like this instead:
def make_users
stores = Store.all(limit: 8)
99.times do |n|
password = "password"
stores.each do |store|
first_name = Faker::Name.first_name
last_name = Faker::Name.last_name
email = "#{store.name.parameterize}-#{n+1}#example.org"
store.users.create!(first_name: first_name,
last_name: last_name,
email: email,
password: password,
password_confirmation: password)
end
end
end
Now the first 8 users will have an email like (some-store-name)-1#example.org, next 8 will have (some-store-name)-2#example.org and so on.

Resources