how to turn off Cyclomatic complexity for where is too high - ruby

I have a where method in model that's throwing lint error. The whole code in model is just a test code at this moment and will be refactored later on. So i want to turn off this lint error for now.
UPDATE:
Here's the method i am getting lint error at
def self.where(start_date, end_date, customer_id, type, location, is_registered)
filtered_data = if start_date && end_date
customers.select do |e|
e[:startDateTime].to_datetime >= start_date.to_datetime &&
e[:endDateTime].to_datetime <= end_date.to_datetime
end
elsif start_date
customers.select {|e| e[:startDateTime].to_datetime >= start_date.to_datetime }
elsif end_date
customers.select {|e| e[:endDateTime].to_datetime <= end_date.to_datetime }
else
customers
end
if !is_registered.nil? # is_registered is true or false
filtered_data = customers.select { |e| e[:isRegistered].to_s == is_registered }
end
# Check if hash presents and check if the keys have valid values.
if customer_id || type || location
hash = { customerId: customer_id.to_i, type: type, location: location }
# delete if type, location or customer_id is nil.
hash = hash.delete_if { |_k, v| v.nil? || v == 0 }
keys = hash.keys
filtered_data = filtered_data.select { |h| h.select { |k| keys.include?(k) } == hash }
else
filtered_data
end
filtered_data.map do |slot|
mock_customer(slot[:id], slot[:customerId], slot[:name], slot[:startDateTime],
slot[:endDateTime], slot[:location], slot[:status])
end
end
I tried adding # rubocop:disable Metrics/AbcSize in model but didnt help.

Try this:
# rubocop:disable Metrics/CyclomaticComplexity
.... your method here
# rubocop:enable Metrics/CyclomaticComplexity
Also, if you want to turn off Rubocop for all those test files (since you are going to refactor them), you can try this answer.

This works for me
# rubocop:disable Style/CyclomaticComplexity
yor code
# rubocop:enable Style/CyclomaticComplexity

Related

Converting Ruby Hash into string with escapes

I have a Hash which needs to be converted in a String with escaped characters.
{name: "fakename"}
and should end up like this:
'name:\'fakename\'
I don't know how this type of string is called. Maybe there is an already existing method, which I simply don't know...
At the end I would do something like this:
name = {name: "fakename"}
metadata = {}
metadata['foo'] = 'bar'
"#{name} AND #{metadata}"
which ends up in that:
'name:\'fakename\' AND metadata[\'foo\']:\'bar\''
Context: This query a requirement to search Stripe API: https://stripe.com/docs/api/customers/search
If possible I would use Stripe's gem.
In case you can't use it, this piece of code extracted from the gem should help you encode the query parameters.
require 'cgi'
# Copied from here: https://github.com/stripe/stripe-ruby/blob/a06b1477e7c28f299222de454fa387e53bfd2c66/lib/stripe/util.rb
class Util
def self.flatten_params(params, parent_key = nil)
result = []
# do not sort the final output because arrays (and arrays of hashes
# especially) can be order sensitive, but do sort incoming parameters
params.each do |key, value|
calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s
if value.is_a?(Hash)
result += flatten_params(value, calculated_key)
elsif value.is_a?(Array)
result += flatten_params_array(value, calculated_key)
else
result << [calculated_key, value]
end
end
result
end
def self.flatten_params_array(value, calculated_key)
result = []
value.each_with_index do |elem, i|
if elem.is_a?(Hash)
result += flatten_params(elem, "#{calculated_key}[#{i}]")
elsif elem.is_a?(Array)
result += flatten_params_array(elem, calculated_key)
else
result << ["#{calculated_key}[#{i}]", elem]
end
end
result
end
def self.url_encode(key)
CGI.escape(key.to_s).
# Don't use strict form encoding by changing the square bracket control
# characters back to their literals. This is fine by the server, and
# makes these parameter strings easier to read.
gsub("%5B", "[").gsub("%5D", "]")
end
end
params = { name: 'fakename', metadata: { foo: 'bar' } }
Util.flatten_params(params).map { |k, v| "#{Util.url_encode(k)}=#{Util.url_encode(v)}" }.join("&")
I use it now with that string, which works... Quite straigt forward:
"email:\'#{email}\'"
email = "test#test.com"
key = "foo"
value = "bar"
["email:\'#{email}\'", "metadata[\'#{key}\']:\'#{value}\'"].join(" AND ")
=> "email:'test#test.com' AND metadata['foo']:'bar'"
which is accepted by Stripe API

Hash concatenation/merge without duplicate and select the max key value?

h1 = {"a"=> "121","b"=> "248","d"=> "192","e"=> "182"}
h2 = {"a"=> "458","b"=> "122","c"=> "562","f"=> "224","g"=> "352"}
This is my input I try to merge it but I had this output only
merge_hash = {"a"=>"121", "b"=>"248", "c"=>"562", "f"=>"224", "g"=>"352", "d"=>"192", "e"=>"182"}
But I want this
merge_hash = {"a"=>"458", "b"=>"248", "c"=>"562", "f"=>"224", "g"=>"352", "d"=>"192", "e"=>"182"}
I used this to merge the Hash
merge_hash = h2.merge(h1)
Anyone please help me with this Issue
It seems that you want to select the maximum value whenever there's an overlap. For this you can use the block form of merge:
h1.merge(h2) do |key, old_val, new_val|
[old_val, new_val].max
end
This block only gets run when there's an overlap and its return value determines which value gets used
I believe that what you are trying to accomplish can be done as follow:
def merge_hashes(h1:, h2:)
keys = h1.merge(h2).keys
hash_result = {}
keys.each do |key|
if h1[key].nil? && !h2[key].nil?
hash_result[key] = h2[key]
elsif !h1[key].nil? && h2[key].nil?
hash_result[key] = h1[key]
else
hash_result[key] = h1[key] > h2[key] ? h1[key] : h2[key]
end
end
hash_result
end
Then you can use it: merge_hashes(h1: h1, h2: h2)
h1.merge(h2) { |_, *value| value.max }

For loop and if in puts function - Ruby

I am trying to use for loop and if condition in creating a file using File.open and puts function. My code is
I want to write these entries only if it is not null. How to do it?
Edit: Full code is
require 'fileutils'
require 'json'
require 'open-uri'
require 'pp'
data = JSON.parse('data')
array = data
if array &.any?
drafts_dir = File.expand_path('../drats', dir)
FileUtils.mkdir_p(drafts_dir)
array.each do |entry|
File.open(File.join(drafts_dir, "#{entry['twitter']}.md"), 'wb') do |draft|
keys = 1.upto(6).map { |i| "key_#{i}" }
values = keys.map { |k| "<img src='#{entry['image']} alt='image'>" if entry['image']}
# you can also do values = entry.values_at(*keys)
str = values.reject do |val|
val.nil? || val.length == 0
end.join("\n")
draft.puts str
end
end
end
I need the the file `mark.md` as
https://somesite.com/image.png' alt='image'>
https://twitter.com/mark'>mark
and `kevin.md` likewise.
you can build the string from an array, rejecting the null values:
keys = 1.upto(6).map { |i| "key_#{i}" }
values = keys.map { |k| entry[k] }
# you can also do values = entry.values_at(*keys)
str = values.reject do |val|
val.nil? || val.length == 0
end.join("\n")
draft.puts str
update in response to your changed question. Do this:
array.each do |entry|
File.open(File.join(drafts_dir, "#{entry['twitter']}.md"), 'wb') do |draft|
next unless ['image', 'twitter'].all? { |k| entry[k]&.length > 1 }
str = [
"<img src='#{entry['image']} alt='image'>",
"<a href='https://twitter.com/#{entry['twitter']}'>#{entry['twitter']}</a>"
].join("\n")
draft.puts str
end
end
Assuming, your entry is hash.
final_string = ''
entry.each_value { |value| final_string << "#{value}\n" }
puts final_string

Rewrite method using case in Ruby

So I have defined this class File inside a module and what I want to do is rewrite the self.parse method so that it uses case. I'm new to Ruby so this is not straightforward for me. Also the method must contain in it's body no more than 8 lines of code. Any ideas how to do it? Also I asked it on Code Review and they said it was off topic for them.
module RBFS
class File
attr_accessor :content
def initialize (data = nil)
#content = data
end
def data_type
case #content
when NilClass then :nil
when String then :string
when TrueClass , FalseClass then :boolean
when Float , Integer then :number
when Symbol then :symbol
end
end
def serialize
case #content
when NilClass then "nil:"
when String then "string:#{#content}"
when TrueClass , FalseClass then "boolean:#{#content}"
when Symbol then "symbol:#{#content}"
when Integer , Float then "number:#{#content}"
end
end
def self.parse (str)
arr = str.partition(':')
if arr[0] == "nil" then return File.new(nil) end
if arr[0] == "string" then return File.new(arr[2].to_s) end
if (arr[0] == "boolean" && arr[2].to_s == 'true') then return File.new(true) end
if (arr[0] == "boolean" && arr[2].to_s == 'false') then return File.new(false) end
if arr[0] == "symbol" then return File.new(arr[2].to_sym) end
return File.new(arr[2].to_i) if (arr[0] == "number" && arr[2].to_s.include?('.') == false)
return File.new(arr[2].to_f) if (arr[0] == "number" && arr[2].to_s.include?('.') == true)
end
end
end
Example how 'RBFS::File.parse' works:
RBFS::File.parse("string:"Hello world") => #<RBFS::File:0x1c45098 #content="Hello world"> #Tested in irb
I would personally prefer this:
def self.parse(arg)
key, value = arg.to_s.split(':')
{
'nil' => new(nil),
'string' => new(value),
'boolean' => new(value == 'true'),
'symbol' => new(value.to_sym),
'number' => new(value.include?('.') ? BigDecimal(value) : Integer(value))
}[key]
end
Code above is actually of 2 lines, broken into multiple lines for readability sake. However, if using case is a must then you can change your code to this:
def self.parse(arg)
key, value = arg.to_s.split(':')
case key
when 'nil' then new(nil)
when 'string' then new(value)
when 'boolean' then new(value == 'true')
when 'symbol' then new(value.to_sym)
when 'number' then new(value.include?('.') ? BigDecimal(value) : Integer(value))
end
end
In Ruby, case statements test using the case equality method #===. #=== returns true for several different of comparisons beyond the type checking you've already implemented in #serialize and #data_type. For example:
Integer === 1 //=> true
Numeric === 1 //=> true
(1..10) === 1 //=> true
1 === 1 //=> true
With that knowledge, we can construct a #parse method as follows:
def parse(serialized)
type, content = serialized.split(':') # A neat trick that'll make things more readable.
case type
when 'nil'
# ...
when 'string'
# ...
when 'boolean'
# ...
when 'symbol'
# ...
when 'number'
# ...
else
raise "Invalid RBFS file."
end
end
I'm not sure that you'll be able to do this in 8 lines without compromising the file's readability or dropping the error handling step I added at the end (which I highly recommend), but to get close, you can use the when ... then ... syntax.
Below is a suggestion for one way you might write this. It is untested, of course. I made two assumptions:
what you refer to as a[2] is a string, so a[2].to_s is unnecessary.
if a[0] => "boolean", a[2] is 'true' or 'false'.
module RBFS
class File
attr_accessor :content
def initialize (data = nil)
#content = data
end
def data_type
class_to_sym[#content.class]
end
def serialize
return nil if #content.class == NilClass
"#{class_to_sym[#content.class].to_s}:#{#content}"
end
def self.parse (str)
type,_,val = str.partition(':')
File.new(type_to_arg(type, val))
end
private
def class_to_sym
{ NilClass=>:nil, String=>:string, TrueClass=>:boolean,
FalseClass=>:boolean, Numeric=>:number, Symbol=>:symbol }
end
def type_to_arg(type, val)
case type
when "nil" then nil
when "string" then val
when "boolean" then val == 'true'
when "symbol" then val.to_sym
when "numeric" then val[/\./] ? val.to_f : val.to_i
end
end
end
end
end
If you prefer, you could replace val[/\./] with val.include?('.').
You could alternatively use a hash to simulate a case statement in type_to_arg:
def type_to_arg(type, val)
{ "nil" =>nil,
"string" =>val,
"boolean"=>(val=='true'),
"symbol" =>val.to_sym,
"number" =>val[/\./] ? val.to_f : val.to_i
}[type]
end

Checking multiple params in Ruby

These params come out of html inputs in erb templates (this code is in the main application.rb), and I am checking if they are filled before I add them to n.requestusers, which will become part of a database entry. It works, but it feels more like a bash script the way it is now. What would be the best way to write something like this?
a route in the main .rb
if params[:user2].empty? && params[:user3].empty? && params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1]
elsif params[:user3].empty? && params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1], params[:user2]
elsif params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1], params[:user2], params[:user3]
elsif params[:user5].empty?
n.requestusers = params[:user1], params[:user2], params[:user3], params[:user4]
else
n.requestusers = params[:user1], params[:user2], params[:user3], params[:user4], params[:user5]
end
Instead of having all of those conditional statements might you be interested in something like:
n.requestusers = params.select { |key, val| not val.empty? }.values
Or a cleaner way as suggested by #theTinMan:
n.requestusers = params.reject { |key, val| val.empty? }.values
select lets you take all of the none empty parameter values and returns them. values lets you grab those values as an array.
I am not experienced with web frameworks, so my suggestion is a bit of a shot in the dark.
This isn't tested, because there are no sample values to test against, but, after some refactoring I have:
if [:user2, :user3, :user4, :user5].all?{ |s| params[s].empty? }
n.requestusers = params[:user1]
elsif [:user3, :user4, :user5].all? { |s| params[s].empty? }
n.requestusers = [:user1, :user2].map{ |s| params[s] }
elsif [:user4, :user5].all? { |s| params[s].empty? }
n.requestusers = [:user1, :user2, :user3].map{ |s| params[s] }
elsif params[:user5].empty?
n.requestusers = [:user1, :user2, :user3, :user4].map{ |s| params[s] }
else
n.requestusers = [:user1, :user2, :user3, :user4, :user5].map{ |s| params[s] }
end
Looking at that further, this seems sensible:
USER_LIST = [:user1, :user2, :user3, :user4, :user5]
USER_LIST.size.times do |i|
user_list = USER_LIST
get_users = user_list.shift(1 + i)
if user_list.all?{ |s| params[s].empty? }
n.requestusers = params.values_at(get_users)
break
end
end
Like I said, that's not tested, but I'd work with something along those lines.
Adjust USER_LIST as necessary.
I am not that familiar with Sinatra, but if you wanted to add elements to the array, you can just do (assuming n.requestusers has already been initialized):
n.requestusers << params[:user1] unless params[:user1].empty?
You can do that for each user parameter.
Edit: It would probably be better just to check if the param exists since if it isn't found, nil is returned -- calling empty? on nil throws a NoMethodError. It would probably better to do:
n.requestusers << params[:user1] unless params[:user1]
This would ensure that it adds the param if it exists.

Resources