Ruby For loop through JSON response from API - ruby

I have a perfectly functional piece of code that does what I want but it is really heavy and i am sure it could be greatly improved by using a nice For loop somewhere but I'm not sure how to go about it
My code is:
def helper
response = RestClient.get API_RESPONSE
check_X_0 = JSON.parse(response.body)['EXP'][0]['X']
check_Y_0 = JSON.parse(response.body)['EXP'][0]['Y']
check_X_1 = JSON.parse(response.body)['EXP'][1]['X']
check_Y_1 = JSON.parse(response.body)['EXP'][1]['Y']
check_X_2 = JSON.parse(response.body)['EXP'][2]['X']
check_Y_2 = JSON.parse(response.body)['EXP'][2]['Y']
check_X_3 = JSON.parse(response.body)['EXP'][3]['X']
check_Y_3 = JSON.parse(response.body)['EXP'][3]['Y']
check_X_4 = JSON.parse(response.body)['EXP'][4]['X']
check_Y_4 = JSON.parse(response.body)['EXP'][4]['Y']
if check_X_0 == false && check_Y_0 == true
exp_id = JSON.parse(response.body)['EXP'][0]['ABC']
elsif check_X_1 == false && check_Y_1 == true
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
elsif check_X_2 == false && check_Y_2 == true
exp_id = JSON.parse(response.body)['EXP'][2]['ABC']
elsif check_X_3 == false && check_Y_3 == true
exp_id = JSON.parse(response.body)['EXP'][3]['ABC']
elsif check_X_4 == false && check_Y_4 == true
exp_id = JSON.parse(response.body)['EXP'][4]['ABC']
else
puts 'Nothing valid - use default'
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
end
This is fairly cumbersome so can anyone help me to trim this down?

You should not parse your json 10 times in the first place. Parse it once and use the result.
rb = RestClient.get(API_RESPONSE).body['EXP']
checks = (0..4).map { |i, s| [i, rb[i]['X'], rb[i]['Y']] }
exp_id =
if found = checks.detect { |_i, f, t| !f && t }
rb[found.first]['ABC']
else
puts 'Nothing valid - use default'
rb[1]['ABC']
end

Related

How do I verify support for app blocks in Ruby?

In this guide: https://shopify.dev/apps/online-store/verify-support there is a Node.js example. Is there anywhere a Ruby example that does the same thing?
Ok, I did it myself:
themes = ShopifyAPI::Theme.all
publishedTheme = themes.find {|t| t.role == 'main'}
assets = ShopifyAPI::Asset.find(:all, params: {theme_id: publishedTheme.id})
APP_BLOCK_TEMPLATES = ['product', 'collection', 'index']
templateJSONFiles = assets.filter {|file| APP_BLOCK_TEMPLATES.any? {|t| file.key == "templates/#{t}.json"}}
if templateJSONFiles.size === APP_BLOCK_TEMPLATES.size
puts 'All desired templates support sections everywhere!'
elsif templateJSONFiles.size > 0
puts 'Only some of the desired templates support sections everywhere.'
end
templateMainSections = templateJSONFiles.map do |tmp|
a = ShopifyAPI::Asset.find(tmp.key, params: {theme_id: publishedTheme.id})
json = JSON.parse(a.value)
main = json['sections'].find {|k, v| k == 'main' || v['type'].start_with?('main-')}
if main
assets.find {|file| file.key == "sections/#{main[1]['type']}.liquid"}
else
nil
end
end.compact
sectionsWithAppBlock = templateMainSections.map do |file|
acceptsAppBlock = false
asset = ShopifyAPI::Asset.find(file.key, params: {theme_id: publishedTheme.id})
match = asset.value.match(/\{\%\s+schema\s+\%\}([\s\S]*?)\{\%\s+endschema\s+\%\}/m)
schema = JSON.parse(match[1]);
if (schema && schema['blocks'])
acceptsAppBlock = schema['blocks'].any? {|b| b['type'] == '#app'};
end
acceptsAppBlock ? file : nil
end.compact
if sectionsWithAppBlock.size > 0 && templateJSONFiles.size == sectionsWithAppBlock.size
puts 'All desired templates have main sections that support app blocks!'
elsif sectionsWithAppBlock.size > 0
puts 'Only some of the desired templates support app blocks.'
else
puts 'None of the desired templates support app blocks'
end

Rails ActionController::Parameters sanitize

I need to sanitize the strong parameters in Rails5 in Rails 4 I used this:
def forest_hash(hash)
new_hash = hash.deep_dup
new_hash.each do |k, v|
new_hash[k] =
if v.is_a?(Hash)
forest_hash(v)
# elsif v.respond_to?(:to_unsafe_h)
# forest_hash(v.to_unsafe_h)
elsif v.is_a?(String) && DATE_TIME_REGEXP === v
v = Time.zone.parse(v)
elsif v == ''
nil
elsif v == ['']
[]
elsif v == '<p> </p>'
nil
elsif v == 'true'
true
elsif v == 'false'
false
elsif v.is_a?(String) && v.to_i.to_s == v
v.to_i
elsif v.is_a?(Array) && v.count > 1
v.delete_if { |x| x == '' }
elsif v.is_a?(String) && base_helpers.strip_tags(v) != v
Foresttrees::TagSanitizer.new(v).sanitize
elsif v.is_a?(String)
v.squish
else
v
end
if new_hash[k].is_a?(Array) && new_hash[k].all? { |vv| vv.is_a?(Hash) }
binding.pry
new_hash[k].map! { |vv| forest_hash(vv) }
end
end
end
<ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"XXXXXXX", "commit"=>"Save", "activism_tree"=><ActionController::Parameters {"name"=>"qewrqewr", "reason"=>"", "affected_locations"=>["", "global"], "risk"=>"3", "description"=>"<p>qerqewr</p>", "published_at"=>"", "published_internet_at"=>"", "related_tree_ids"=>[""], "target_customer_ids"=>["", "5f75952427e4984019f6e9d4"], "monitored_source"=>"false", "cuter"=>"qerqewrqewr", "cut_duration"=>"", "expected_date"=>"", "cut_probability"=>"", "place"=>"", "hour"=>"", "followers"=>"0", "simplified_evidences_attributes"=>{"0"=>{"name"=>"qerqewrq", "description"=>"", "type"=>"url", "url"=>"qwerqwer", "sanitized"=>"false"}}} permitted: false>, "type"=>"activism", "controller"=>"foresttrees/trees", "action"=>"create", "locale"=>"en"} permitted: false>
The problem is that:
after doing that params.each {​​ |k,v| params[k] = v }​​.class
I get a hash so I loose the permit, required methods,
I tried to use slice and fetch but slice is not really working properly. And I want not loose de ActionController::Parameter Properties in my new object.
so how I can proceed
Thera are many difference between Rails 4 and Rails 5 and one is in Strong parameters:
But with Rails 5, ActionController::Parameters will no longer inherit
from HashWithIndifferentAccess
As pointed here
But if you look at the documentation, you can use other methods that can perform better your task more functionally, without the deep_dup and without using map! and work excusively in the values of the object that you want in this case.
in this case you should use transform_values also alternatively exists with !.
One thing important is that v can be a scalar (String, number ...), Array or Hash or another (ActiveController::Parameters). so you can process like this:
def forest_hash(hash)
hash.transform_values do |v|
if v.is_a?(Hash) || v.is_a?(ActionController::Parameters)
forest_hash(v)
elsif v.is_a?(String) && DATE_TIME_REGEXP === v
Time.zone.parse(v)
elsif v == ''
nil
elsif v == ['']
[]
elsif v == '<p> </p>'
nil
elsif v == 'true'
true
elsif v == 'false'
false
elsif v.is_a?(String) && v.to_i.to_s == v
v.to_i
elsif v.is_a?(Array)
v.delete_if { |x| x == '' }.map do |vv|
vv.is_a?(Hash) || vv.is_a?(ActionController::Parameters) ? forest_hash(vv) : vv
end
elsif v.is_a?(String) && base_helpers.strip_tags(v) != v
Foresttrees::TagSanitizer.new(v).sanitize
elsif v.is_a?(String)
v.squish
else
v
end
end
end
You should also, maybe encapsulate some value manipulation the scalar String and Array (hash ActiveController::Parameters always recursive) in a separate method, to made this code more ruby like and no need of the infinite if else chain.
Whith this code you return a new ActionController::Parameters that applies the sanitazions or modifications on the values

Why one solution prints true when the other prints false?

I have two solutions to reverse a string in Ruby. One prints true while the other prints false, however, both print out the response I want.
Why does one say it's false even though it results in the same answer as the solution that prints true?
Here are the solutions and the tests:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
puts new
end
def secondreverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = string[i] + new
i += 1
end
return new
end
These are tests to check that the code is working. After writing your solution, they should all print true.
puts("\nTests for #reverse")
puts("===============================================")
puts(
'secondreverse("abc") == "cba": ' + (secondreverse("abc") == "cba").to_s
)
puts(
'secondreverse("a") == "a": ' + (secondreverse("a") == "a").to_s
)
puts(
'secondreverse("") == "": ' + (secondreverse("") == "").to_s
)
puts("===============================================")
In your #reverse function, you are returning puts new when you should just be returning new.
As you can see from the example below, puts returns nil after it prints to the screen:
irb(main): puts 'test'
test
=> nil
If you change puts new to just new, it works as you expect.
Aside
You don't need to use explicit return calls. In Ruby, the last line executed will be returned, so you can replace this in both methods:
return new
with:
new
The problem is that in the reverse method, you are printing the value to stdout using the puts method but you are not returning it (your method returns nil instead). When you compare nil == "cba" it returns false. You have to return the new variable:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
new
end

How to DRY out this ruby code

I would like to DRY out this code. I can't seem to figure out how though.
def get_all_verb_nodes
all_verb_nodes = #all_nodes_ins_del_nodes.select { |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
words = get_node_text(node)
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words.length <= 2 && words.any? { |word| is_a_verb?(base_word+word) || is_a_verb?(word) && !is_pluralized?(base_word+word, base_word+next_node_word) } && !next_node_is_insert_or_delete
}
end
def get_all_article_nodes
all_article_nodes = #all_nodes_ins_del_nodes.select { |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
words = get_node_text(node)
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words.length <= 2 && words.any? { |word| #articleset.include?(word) || (#articleset.include?(base_word) if word == 'n') } && !#articleset.include?(next_node_word) && !next_node_is_insert_or_delete
}
end
Both are almost identical except for the last line which defines the specific requirement of the function.
Any ideas appreciated.
Here is a first cut at it. I moved things around so they were grouped logically and to facilitate the yield.
def get_all_nodes
#all_nodes_ins_del_nodes.select do |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words = get_node_text(node)
words.length <= 2 &&
!next_node_is_insert_or_delete &&
yield(words, base_word, next_node_word)
end
end
all_verb_nodes = get_all_nodes do |words, base_word, next_node_word|
words.any? do |word|
is_a_verb?(base_word + word) ||
is_a_verb?(word) &&
!is_pluralized?(base_word + word, base_word + next_node_word)
end
end
all_article_nodes = get_all_nodes do |words, base_word, next_node_word|
!#articleset.include?(next_node_word) &&
words.any? do |word|
#articleset.include?(word) || (#articleset.include?(base_word) if word == 'n')
end
end

Rubocop:method has too many lines

Hello I am new at ruby programming.
Ran rubocop inspection in my project and it says:
Method has too many lines. [13/10] def refresh_status
Here is my methods:
def refresh_status
lost = false
in_progress = false
won = false
#bets.each do |bet|
lost = true if bet.result == :lost
if bet.result == :canceled
#to_return /= bet.odd
won = true
end
in_progress = true if bet.result == :in_progress
won = true if bet.result == :won
end
def_result_after_refresh(lost, in_progress, won)
end
def def_result_after_refresh(lost, in_progress, won)
if lost
#result = :lost
elsif in_progress
#result = :in_progress
elsif won
#result = :won
end
end
Can't find a way to make that method shorter, maybe you could help?
You can use some the Enumerable methods.
def refresh_status
#to_return /= #bets.select { |bet| bet.result == :canceled }.map(&:odd).reduce(1, :*)
results = #bets.map { |bet| bet.result == :cancelled ? :won : bet.result }.uniq
#result = case
when results.include?(:lost) then :lost
when results.include?(:in_progress ) then :in_progress
when results.include?(:won) then :won
end
end

Resources