How to refactor ruby code? [closed] - ruby

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
Hi i have a code i would like to refactor
def gear_type
#gear_type ||= self.class.gear_types.find{|gears| gears["id"]==source["gear_type_id"]}["text"] if source["gear_type_id"]
end
def brand
#brand ||= self.class.brands.find{|node| node["id"]==source["brand_id"].to_s}["text"] if source['brand_id']
end
what is best way? to use eval or define method? i've tried this but there are some error i can't discover yet:
%w(gear_type brand).each do |meth|
define_method(meth){
instance_variable_get("##{meth}") rescue
instance_variable_set("##{meth}", self.class.send(meth.pluralize).find{|node| node["id"]==source["#{meth}_id"]}["text"]) if source["#{meth}_id"]
}
end

I'd just write a common finder method that you can parameterize:
def gear_type
#gear_type ||= generic_finder :gear_types, "gear_type_id"
end
def brand
#brand ||= generic_finder :brands, "brand_id"
end
def generic_finder(collection, primary_key)
self.class.send(collection).each do |object|
return object["text"] if object["id"] == source[primary_key]
end if source[primary_key]
nil
end

instance_variable_get("##{meth}") does not raise an error if the instance variable is not set, it returns nil. So you have to do almost the same you were doing:
%w(gear_type brand).each do |meth|
define_method(meth){
instance_variable_get("##{meth}") || instance_variable_set("##{meth}", self.class.send(meth.pluralize).find{|node| node["id"]==source["#{meth}_id"]}["text"]) if source["#{meth}_id"]
}
end
You should also refactor that line. It has to many stuff on it
%w(gear_type brand).each do |meth|
def source(meth)
#source ||= source["#{meth}_id"]
end
def class_meths(meth)
self.class.send(meth.pluralize)
end
def look_for(meth)
class_meths(meth).find{|node| node["id"] == source(meth)}["text"]
end
define_method(meth) do
value = instance_variable_get("##{meth}")
instance_variable_set("##{meth}", look_for(meth)) if !value && source(meth)
end
end
Here is a try. Not sure if it got better or not, but it's easier to read I think.
Oh! I just realized those methods probably won't be on the scope when the meta? method is called. But oh well, it's still a good example I think :)

It's probably cleaner just to use eval:
%w(gear_type brand).each do |meth|
eval <<-RUBY
def #{meth}
##{meth} ||= self.class.#{meth.plural}.find{|item| item["id"]==source["#{meth}_id"]}["text"] if source["#{meth}_id"]
end
RUBY
end

Related

Putting Variables Between | | in Ruby [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
In Ruby code, I noticed some variables or other objects placed in between | |. I have no idea why. Is it usually used in hashmaps?
These are arguments to a closure, a little anonymous function.
my_method do |argument1, argument2|
puts argument1, argument2
end
|argument1, argument2| says it takes two arguments. Just like if you wrote a method you'd say def method(argument1, argument2). Then my_method can accept the closure using & and call it using call.
def my_method(&block)
block.call(23, 42)
end
This is how Ruby does iterators. For example, here's how you'd implement map.
def my_map(list, &block)
list.each do |element|
block.call(element)
end
end
my_map([23, 42, 99]) do |element|
puts element
end
Or you can use yield which implicitly calls the block and it's slightly faster.
def my_map(list)
list.each do |element|
yield element
end
end
A method can check if a block was passed in with block_given?. This is how, for example, File.open can either return an open filehandle, or it can give it to a block and close it when the block is done.
def open(filename, mode='r')
file = File.new(filename, mode)
if block_given?
yield file
file.close
else
file
end
end
# open foo.txt
puts open("foo.txt")
# open foo.txt, execute the block, and close it
open("foo.txt") do |file|
puts file.read
end
This is very useful for when you need to take action, such as closing a file or shutting down a connection, once the work is done,
For more see Block Arguments in the Ruby Docs.

Is this a good metaprogramming practice? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I wanted to make a simple DSL where I could pass a bunch of methods to a block, relying on self as the implicit receiver. So basically here you can call the 'say' method on this class object, passing it 'things to say' as methods in the block. The last line returns ['Maria']. I was wondering if this is a good programming practice for creating DSLs and if there are any problems with this approach.
class SaySomething
def initialize
#said = []
end
def hey(name)
#said << name
end
def say(&block)
instance_eval(&block)
end
end
a = SaySomething.new
name = 'Maria'
a.say do
hey(name)
end
a.instance_eval { p #said } #=> produces ['Maria']
I would probably add an attr_accessor :said and then replace your last line with
a.said
#=> ['Maria']
Other than that your code looks fine to me. If you want to learn more about metaprogramming in Ruby, I can recommend the book "Eloquent Ruby".
The only problem with this approach is that any class variables will collide with variables in the same scope as the block. The usual approach is to provide instance evaluation, but also allow the user to specify the class as an argument as a fall back.
class Test
def test; "hello"; end
def say(&b)
if b.arity == 1
b.call(self)
else
instance_eval &b
end
end
end
t = Test.new
test = "fred"
t.say { p test } # "fred"
t.say { |t| p t.test } # "hello"

Ruby - Can I Move This Logic To a Global Variable? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
In an instance, in a method, I loop through a list lines and manipulate each line. However, there are some lines which I want to skip. I'd like to define which lines to skip with some global variables at the top of the instance. Is that possible? How can I do that?
class Bets
#stuff
def make_prediction
lines.each do |line|
next if #league == :nba && line[:bet_type] == :total && line[:period] == :h1
next if [:total, :spread, :money_line].include?(line[:bet_type]) && line[:period] == :fg
#do stuff
end
end
end
EDIT:
someone voted for this topic to be closed as unhelpful cause it's unclear. I'm not sure what is unclear about it. But I'll make it more explicit how I want it to look...
class Bets
#psuedo code, obviously this wont work
#and i cant think how to make it work
#or if its even possible
GLOBAL = true if #league == :nba & line[:bet_type] == :total & line[:period] == :h1
#stuff
def make_prediction
lines.each do |line|
next if GLOBAL #psuedo code
#do stuff
end
end
end
What about using methods:
class Bets
def skip?
#league == :nba & line[:bet_type] == :total & line[:period] == :h1
end
#stuff
def make_prediction
lines.each do |line|
next if skip?
#do stuff
end
end
end
Global variables are largely frowned upon, so try to find a context where your test make sense.
Try creating a Proc and executing it in the instance's context
GLOBAL = Proc.new {|line| your_code_goes_here}
#...
#...
def make_prediction
lines.each do |line|
next if instance_exec(line,GLOBAL) #psuedo code
#do stuff
end
end

Ruby program do not launch [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm new to ruby and I'am building a little program on ruby alone,the problem is that I'am trying to launch it without success
Imagine that i have this code
#usr/bin/ruby
Class Whatever
def get_user_input
#user_input = gets.chomp
user_doing(#user_input)
end
def user_doing
#something
end
end
What I want is to call the get_user_input method as soon as i feed my rb file to ruby
I tried to call it on a initialize method
def initialize
get_user_input
end
I also tried to define it as a "class method"
def get_user_input
#user_input = gets.chomp
user_doing(#user_input)
end
but neither of them seems to work as when I'm start the rb file the program doesn't expect my input so how can i do this?.
You just define a class. What you did not call the method. Just add Whatever.new.get_user_input to your file.
#usr/bin/ruby
class Whatever
def initialize(input)
#input = input
end
def self.get_user_input
whatever = new(gets.chomp)
whatever.user_doing
end
def user_doing
puts "Input was: #{#input}"
end
end
Whatever.get_user_input
Btw: Your user_doing does not take args in the moment. You may want to check that.

Feature flags best practice: condition inside or outside of a method? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
We're using feature flags in order to enable/disable certain features in our system.
I had a discussion with my colleague over what's the standard way of adding feature flags to the code itself:
Consider the following method:
def featured_method
do_this
do_that
end
The method is being called from about 15 different places inside our code.
Would you recommend adding the check if the feature is enabled before every call to this method:
if feature_enabled?(:feature_key)
featured_method
end
Or inside the featured_method itself, like this:
def featured_method
if feature_enabled?(:feature_key)
do_this
do_that
end
end
The advantage of having the condition inside the method itself is obvious: DRYing up the code, and the fact that when you want to add the feature permanently, you simply remove the condition from within the method.
The advantage of having the condition before every call is that it is very clear whether that method gets executed or not without going into the featured_method code itself, which can save quite a lot of headaches.
I was wondering if there's another solution or a standard for those kind of issues.
I would merge both approaches.
This would lead to DRY code on the callers side. It would not violate the SRP in the feature_method and it would clearly communicate what is going on - if you can find a better name than me:
def may_execute_featured_method
featured_method if feature_enabled?(:feature_key)
end
def featured_method
do_this
do_that
end
The caller would use may_execute_featured_method
I would be tempted to split feature keying out into its own module, and use it like this:
class Foo
include FeatureKeyed
def foo
'foo'
end
feature_keyed :foo
def bar
'bar'
end
feature_keyed :bar
end
foo = Foo.new
p foo.foo # => "foo"
p foo.bar # => FeatureKeyed::FeatureDisabled
Here's the module:
module FeatureKeyed
class FeatureDisabled < StandardError ; end
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def feature_keyed(method_name, feature_key = method_name)
orig_method = instance_method(method_name)
define_method method_name do |*args|
raise FeatureDisabled unless feature_enabled?(feature_key)
orig_method.bind(self).call *args
end
end
end
def feature_enabled?(feature_key)
feature_key == :foo
end
end
Notes:
feature_enabled? hard-codes the eneabled feature names. You would change that.
This code raises an exception if a feature is disabled. The code in your question simply returns. Do what makes sense for your application. If you need different "not enabled" behavior for different methods, then the behavior could be passed to feature_keyed.
method _feature_keyed_ will take a second argument which is the feature key. If missing, the name of the method is used as the feature key.

Resources