delete last value of a splitted string - ruby-on-rails

I want to look into a hash but the last value should be there :
params send to controller
{
"user"=>{"email"=>"a#a.fr,
lol#lol.fr"}
method
def set_users
#users = params[:user][:email]
logger.debug "#{#users.split(",").each { |e| puts e }}"
end
logs
a#a.fr
lol#lol.fr
["a#a.fr", " lol#lol.fr"]
The point is that #each takes a value (["a#a.fr", " lol#lol.fr"]) which is not in the hash. How can I apply #each to the whole hash except on this value

I'd do:
#users.split(",")[0..-2]
Namely, take everything but the last.
So:
logger.debug #users.split(",")[0..-2].join(', ')
Seems you just need:
logger.debug #users

You should use the String#gsub method like this:
def set_users
email = params[:user][:email]
params[:user][:email] = email.gsub(/,.*/i, '')
end

Rewrite your method:
def set_users
#users = params[:user][:email].split(',')
#users.pop
end
Then you can use #users without last one and get last one's info by the return value from set_users method.

Related

Collect KeyErrors from Ruby hash into array

I need to extract multiple fields from hash. But I respect my client and I want to gather all missed fields instead of returning it one by one. My idea was to use #fetch, intercept error with KeyError, put error.key into instance variable array and return proper error explanation with full list of missed keys.
Something like that
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
return 'Error'
end
private
def extract_values
{
value_1: #hash.fetch(:required_field_1),
value_2: #hash.fetch(:required_field_2),
value_3: #hash.fetch(:required_field_3)
}
end
end
When I try to process hash without required fields I got 'Error' after the first missed field:
pry(main)> Extractor.new(hash: {}).call
Field was missed
=> "Error"
Any clues?
DrySchema and other hash validators are not an option.
An issue with the provided solution is that the extracted values are never returned in the happy path (which presumably is important?). The call method is also stateful / non-idempotent. Subsequent calls to call will duplicate the missing-keys.
Finally - not sure how it's being used, but I don't love a method that returns either a hash or a string.
An alternative that attempts to follow a more functional pattern might look like:
class Extractor
attr_reader :hash, :missed_keys, :required_keys
def initialize hash
#hash = hash
#missed_keys = []
#required_keys = [:required_field_1, :required_field_2, :required_field_3]
end
def call
validate_keys_exist!
extract_values
end
private
def validate_keys_exist!
missed_keys = find_missing_keys
raise MissingKeysError, "Missed keys: #{missed_keys.join(', ')}" if missed_keys.any?
end
def find_missing_keys
required_keys - hash.keys
end
def extract_values
hash.slice(*required_keys)
# not sure if you need to map the keys to new values.
# if so you can iterate over a hash of `from: :to` pairs instead of the
# required_keys array.
end
end
Ok, I got it. The reason is in intercept level and method closures.
In aforementioned implementation Ruby tried to execute call method, got an error and exits.
If we rework it like that:
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
end
private
def extract_values
{
value_1: #hash.fetch(:required_field_1),
value_2: #hash.fetch(:required_field_2),
value_3: #hash.fetch(:required_field_3)
}
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
nil
end
end
it looks better, but still not what we wanted:
pry(main)> Extractor.new(hash: {}).call
Field was missed
=> "Missed keys: required_field_1"
This is because ruby tried to execute extract_values method, encounters first missed value and exits
So the solution as follow:
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
end
private
def extract_values
{
value_1: fetch_value(:required_field_1),
value_2: fetch_value(:required_field_2),
value_3: fetch_value(:required_field_3)
}
end
def fetch_value(key)
#hash.fetch(key)
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
nil
end
end
Extractor.new(hash: {}).call
Field was missed
Field was missed
Field was missed
=> "Missed keys: required_field_1, required_field_2, required_field_3"
Error interception is accomplished on the fetch_value level and Ruby skips required values one by one

routing and searching a db ruby

I want to retrieve all the tweets that have a certain hashtag in them.
At first I add the hashtags in my 2 tables :
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/){ |tag|
#allhashes = Hashtag.all
#hash = Hashtag.find_by_name(tag[0].strip)
unless #hash
#hashtag = Hashtag.new(name: tag[0].strip)
#hashtag.save
#hashrel = Hashrelation.new(tweet_id: tweet.id, hashtag_id: #hashtag.id)
#hashrel.save
else
#hashrel = Hashrelation.new(tweet_id: tweet.id, hashtag_id: #hash.id)
#hashrel.save
end
}
end
then I want to route to the show method of tweet controller :
get 'tweets/show/(.:format)' => 'tweets#show', as: :hashtag
The links in the hashtags are as follows:
def twitify(tweet = '')
tweet.gsub(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/) do |tag|
" " + link_to("#{tag.strip}", hashtag_path(tag.strip), {:name => tag.strip})
end.html_safe
end
And finally the show method of the tweet controller is :
def show
#hashtag = Hashtag.find_by_name(params[:name])
#tweet_ids = Hashrelation.find_by_hashtag_id(#hashtag.id)
#feed_items = Tweet.find_by_id(#tweets_ids.id)
end
When I click on the link I get :
undefined method `id' for nil:NilClass
which means that params[:name] is either nill or it isn't like the one I have in the DB.
Could you guys help me figure this out ?
The link I see that is called is 'http://localhost:3000/tweets/show/.%23dadawea' which means I have extra things why would I ?.
I would do the following
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/).flatten.each do |tag|
hashtag = Hashtag.where(name: tag.strip).first_or_create
Hashrelation.create(tweet_id: tweet.id, hashtag_id: hashtag.id)
end
end
Then change the twitify method to look like
def twitify(tweet = '')
tweet.gsub(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/) do |tag|
" " + link_to("#{tag.strip}", hashtag_path(name: tag.strip))
end.html_safe
end
And the show method
def show
#hashtag = Hashtag.find_by_name(params[:name])
#tweet_ids = Hashrelation.where(hashtag_id: #hashtag.id).pluck(:id)
#feed_items = Tweet.where(tweet_id: #tweets_ids)
end
This should be what you are looking for. Now for whats changing:
Removed Duplicate logic in the add_hashtags to use create instead.
twitify method is passing name as an html option not a url option so I fixed that. Right now it thinks you want to set the format to the name of the hashtag and name the link the name of the hashtag**
show method is using find_by which will only return a single result not what you wnat for tweet_ids so i changed it to where clause and just grabbed the ids. Then changes feed_items to search Tweet for all tweet_ids in the Array.
To strip the # just use tag.strip[1..-1]

How to add exception handling to my before_action

I have a before_action method like this:
def current_user
#current_user ||= User.find(:id => session[:id])
end
And I call a method like this:
def get_food user
food = Food.find(:id => user.id)
end
This is fine, but I want to add exception handling.
When the user is nil I want to use #current_user:
def get_food user
food = Food.find(if user is nil i want to use #current_user.id)
end
Of course, I can write it like this:
def get_food user
if user.nil?
food = Food.find(#current_user.id)
else
food = Food.find(user.id)
end
Or, is this the best way?
def get_food user
food = Food.find(user == nil? #current_user.id : user.id)
end
I'm curious is there a better way than adding a simple if statement inside the param?
The shortest one lines I can think of are something like this:
Food.find((user || current_user).id)
Food.find(user.try(:id) || current_user.id)
Food.find(user ? user.id : current_user.id)
Not sure if this is really an impovement in readability. I would prefer something like this:
def get_food(user)
user ||= current_user
Food.find(user.id)
end
You can use ternary operator to make it one line:
user ? Food.find(user.id) : Food.find(#current_user.id)
How about arrays
food = Food.where(id: [#current_user.try(:id),user.id]).first
You can try this:
food = Food.find(user.nil? ? #current_user.id : user.id)
What about default parameters?
def get_food(user = #current_user)
food = Food.find(user.id)
end
It will work if you call it without the parameter
something.get_food # notice the method is called with no params
If you want it working also if you pass nil, you should also add:
def get_food(user = #current_user)
food = Food.find((user || #current_user).id)
end
However is strange that foods and users have the same ids...
Maybe the correct query is:
food = Food.find_by_user_id((user || #current_user).id)
or, if users have more than just one food:
foods = Food.where(user: (user || #current_user)) # rails 4, :user => (user || #current_user) for rails 3
Food.find(user.id rescue #current_user.id)

blank hash from rails controller?

I have a custom method in my rails app that looks like this below.
I have tested the sql in the rails console and it returns a hash like this id => vu.
def self.vu ids
results = User.select('tests.dpi, sum(worker) as vu')
.joins{tests}
.where("tests.dpi in (?)", ids)
.group('tests.dpi')
hash = {}
results.each {|record| hash[record.dpi] = record.vu}
hash
end
For show in my controller I have this:
def show
ids = User.find(params[:id])
logger.debug "test: #{ids.id}"
#See ID at this point and SQL query runs
vu = Hash.new
vu =User.vu ids.id
logger.debug "value: #{vu}"
#Blank
vu = vu[ids.id]
logger.debug "value: #{vu}"
#blank no hash value?
end
I can see the SQL query running correctly, and it works in console, but it doesn't set a value in the hash? What am I obviously missing? Thank you.

How to chain optional Mongoid criteria in separate statements?

I'm trying to chain criteria based on optional rails
parameters.
I want to be able to simultaneously filter based on selected tags as
well as searching.
Here is the current code that works in all situations:
if params[:tag] and params[:search]
#notes = Note.tagged_with_criteria(params[:tag]).full_text_search(params[:search])
elsif params[:tag]
#notes = Note.tagged_with_criteria(params[:tag])
elsif params[:search]
#notes = Note.full_text_search(params[:search])
end
I tried simply using
#notes = Note.tagged_with_criteria(params[:tag]).full_text_search(params[:search])
without the if statement, but then if only one of the params was
given, then all notes are returned.
Each of the methods (tagged_with_criteria and full_text_search) are
returning Note.criteria if their parameter is nil / empty.
Is there a simpler, more elegant way to chain Mongoid criteria like this?
I'd rather keep tacking on criteria one-by-one instead of having to do
the weird "if params[...] and params[...]" thing..
UPDATE: here are the current methods:
def tagged_with_criteria(_tags)
_tags = [_tags] unless _tags.is_a? Array
if _tags.empty?
criteria
else
criteria.in(:tags => _tags)
end
end
def self.full_text_search(query)
if query
begin
regex = /#{query}/
# supplied string is valid regex (without the forward slashes) - use it as such
criteria.where(:content => regex)
rescue
# not a valid regexp -treat as literal search string
criteria.where(:content => (/#{Regexp.escape(query)}/))
end
else
# show all notes if there's no search parameter
criteria
end
end
In a situation like that, I would modify the scopes to do nothing when passed in blank values.
I think what might be happening is you are getting empty strings from the params hash, which is causing your code to think that something was entered. Try the scopes with these edits.
def tagged_with_criteria(_tags)
_tags = Array.wrap(_tags).reject(&:blank?)
if _tags.empty?
criteria
else
criteria.in(:tags => _tags)
end
end
def self.full_text_search(query)
if query.present?
begin
regex = /#{query}/
# supplied string is valid regex (without the forward slashes) - use it as such
criteria.where(:content => regex)
rescue
# not a valid regexp -treat as literal search string
criteria.where(:content => (/#{Regexp.escape(query)}/))
end
else
# show all notes if there's no search parameter
criteria
end
end

Resources