ruby,dynamic,methods,eval , Dynamically Generating Code


Dynamically Generating Code

Question:

Tag: ruby,dynamic,methods,eval

I have a whole bunch of similarly-structured methods, each of which look something like this:

def my_method_1
  if params[:user_id]
    #code that stays the same across my_method_1, 2, 3, 4, etc.
    #code that varies across my_method_1, 2, 3, 4, etc.
  elsif params[:tag_id]
    #code that stays the same across my_method_1, 2, 3, 4, etc.
    #code that varies across my_method_1, 2, 3, 4, etc.
  else
    #code that stays the same across my_method_1, 2, 3, 4, etc.
    #code that varies across my_method_1, 2, 3, 4, etc.
  end
end

I have my_method_2, 3, 4, etc. What I'm trying to do is avoid typing out all of this for every method I have, since most of the code is repetitive. I only want to type out the bit of code that actually varies across the methods 1, 2, 3, 4, etc.

My attempt at this uses eval(), which works, and certainly dries up all my individual methods, but makes me uncomfortable. Basically, I have a helper method that takes in key-value pairs, the key being the "context", and the value being a "statement" specified as a string:

def helper_method
  hash.each do |context, statement|
    if params[eval(":#{context}_id")]
      #code that stays the same
      eval(statement)
      return
    end
  end
  eval(hash[:none])
end

Now my individual methods can be super dry, just calling the helper methods and passing in the strings of code:

def my_method_1
  helper_method(
    user:   '#code that varies',
    tag:    '#code that varies',
    none:   '#code that varies'
  )
end

Again, typing out chunks of code inside strings makes me uncomfortable. Any help in going about this another way would be much appreciated!


Answer:

The repetitive branches in your code tell me your class could use a little refactoring to remove the need for multiple if-statements. It sounds like your class needs to delegate to another class for specific functionality. While I don't know that your class looks like, or it's intent, the following is an example that you could apply to your code so that you don't need to generate dynamic methods at all.

The hypothetical Order class with repetitive if-statements

Consider this Order class with multiple, similar looking if-statements:

class Order
  attr_accessor :order_type

  def discount_amount
    if order_type == 1
      .2
    elsif order_type == 2
      .5
    else
      0
    end
  end

  def discount_end_date
    if order_type == 1
      DateTime.new(2014, 12, 31)
    elsif order_type == 2
      DateTime.new(2014, 3, 31)
    else
      # Always expires 100 years from now
      DateTime.new(DateTime.now.year + 100, 1, 1)
    end
  end
end

We have three discounts: 20% off that expires end of 2014; 50% which expires end of March, 2014. Finally the default discount of 0% always expires 100 years in the future. Let's clean this up to remove the if-statments, and instead delegate to a Discount class for these calculations.

Refactor the Order class to utilize delegate methods

First up, let's clean up the Order class, then we will implement a Discount class:

class Order
  attr_accessor :order_type

  def discount
    @discount ||=
      if order_type == 1
        Discount.twenty_percent_off
      elsif order_type == 2
        Discount.half_off
      else
        Discount.default_discount
      end
  end

  def discount_amount
    discount.amount
  end

  def discount_end_date
    discount.end_date
  end
end

Nice and clean. An Order object needs a Discount object to get the discount amount and end date. The Order class is now virtually infinitely extensible since the logic for calculating discounts is off-loaded to another class entirely. The Order#order_type value determines the discount. Now, let's define our Discount class.

Implementing the Discount class

According to our (fake) business rules, only three discounts exist:

  1. 20%-off, expiring end of 2014
  2. 50%-off, expiring end of March, 2014
  3. 0%-off (no discount) which always expires 100 years from today, essentially meaning it never expires

We don't want people to create arbitrary discounts, so let's limit our Discount instances to only those that we define by using a private constructor, then declaring static methods for each kind of discount:

class Discount
  private_class_method :new

  def self.default_discount
    @@default_discount ||= new(0)
  end

  def self.half_off
    @@half_off_discount ||= new(.5, DateTime.new(2014, 3, 31))
  end

  def self.twenty_percent_off
    @@twenty_percent_off ||= new(.2, DateTime.new(2014, 12, 31))
  end

  def initialize(amount, end_date = nil)
    @amount = amount
    @end_date = end_date
  end

  def amount
    @amount
  end

  def end_date
    @end_date ||= DateTime.new(DateTime.now.year + 100, 1, 1)
  end
end

Trying to run Discount.new(...) should throw an error. We only have three Discount instances available:

Discount.half_off
Discount.twenty_percent_off
Discount.default_discount

Given that the Order#order_type is used to determine the discount, we emulate this with Order#discount returning the proper Discount instance based on the Order#order_type. Furthermore, we prevent people from gaming the system by defining their own discounts and the logic for all the discounts is in one class.

order = Order.new
order.order_type = 1
puts order.discount_amount # -> .2

order = Order.new
order.order_type = 2
puts order.discount_amount # -> .5

You can use sub classing to create even more specific business logic, for instance a "random" discount:

class Discount
  protected_class_method :new

  ...

  def self.random
    @random_discount ||= RandomDiscount.new(nil)
  end

  class RandomDiscount < Discount
    def amount
      rand / 2
    end
  end
end

Now Discount.random.amount outputs a different discount every time. The possibilities become endless.

How this applies to your question

The existence of repetitive if-statements means your class is doing too much. It should be delegating to another class that specializes in one of those branches of code. You shouldn't have to manipulate methods in Ruby at runtime to achieve this. It's too much "magic" and is confusing to new developers. With the approach I outlined above, you get a strongly typed definition of what these discounts are, and you keep each class focused on one task (and no, "strongly typed" is not a four letter word in Ruby when properly used). You get well defined relationships between objects, code that is easier to test and you get strong enforcement of business rules. All with no "magic."


Related:


Get the actual value of a boolean attribute


ruby,page-object-gem,rspec3,rspec-expectations
I have the span: <span disabled="disabled">Edit Member</span> When I try to get the value of the disabled attribute: page.in_iframe(:id => 'MembersAreaFrame') do |frame| expect(page.span_element(:xpath => "//span[text()='Edit Member']", :frame => frame).attribute('disabled')).to eq("disabled") end I get: expected: "disabled" got: "true" How do I get the value of specified attribute instead of a...

Map with accumulator on an array


ruby,inject
I'm looking to create a method for Enumerable that does map and inject at the same time. For example, calling it map_with_accumulator, [1,2,3,4].map_with_accumulator(:+) # => [1, 3, 6, 10] or for strings ['a','b','c','d'].map_with_accumulator {|acc,el| acc + '_' + el} # => ['a','a_b','a_b_c','a_b_c_d'] I fail to get a solution working. I...

Call method to generate arguments in ruby works in 1.8.7 but not 1.9.3


ruby-on-rails,ruby,ruby-1.9.3
This is something that I had working in ruby 1.8.7, but no longer works in 1.9.3, and I am not sure what changes make this fail. Previously, I had something like this myFunction(submitArgs()) where submitArgs was a helper method that could be called with some options def submitArgs(args={}) #Some logic/manipulations...

Make instance variable accessible through hash in Ruby


ruby-on-rails,ruby,ruby-on-rails-4,activerecord
In Rails, ActiveRecord objects, attributes are accessible via method as well as through Hash. Example: user = User.first # Assuming User to be inheriting from ActiveRecord::Base user.name # Accessing attribute 'name' via method user[:name] # Attribute 'name' is accessible via hash as well How to make instance variables accessible through...

Rails less url path change


ruby-on-rails,ruby,url,path,less
Developing a Rails application with the less-rails gem I found something unusual : // app/assets/common/css/desktop/typo.less @font-face{ font-family:'SomeFont'; src:url("fonts/db92e416-da16-4ae2-a4c9-378dc24b7952.eot?#iefix"); // ... } The requested font is app/assets/common/css/fonts/db92e416-da16-4ae2-a4c9-378dc24b7952.eot This font is compiled with less and the results is : @font-face { font-family: 'SomeFont'; src: url("desktop/fonts/db92e416-da16-4ae2-a4c9-378dc24b7952.eot?#iefix"); //... } Do you know why is...

How to pivot array into another array in Ruby


arrays,ruby,csv
I have a multidimensional array like this one : myArray = [["Alaska","Rain","3"],["Alaska","Snow","4"],["Alabama","Snow","2"],["Alabama","Hail","1"]] I would like to end up with CSV output like this. State,Snow,Rain,Hail Alaska,4,3,nil Alabama,2,nil,1 I know that to get this outputted to CSV the way I want it I have to have output array like this: outputArray =[["State","Snow","Rain","Hail"],["Alaska",4,3,nil],["Alabama",2,nil,1]]...

Rails shared controller actions


ruby-on-rails,ruby,ruby-on-rails-4
I am having trouble building a controller concern. I would like the concern to extend the classes available actions. Given I have the controller 'SamplesController' class SamplesController < ApplicationController include Searchable perform_search_on(Sample, handle: [ClothingType, Company, Collection, Color]) end I include the module 'Searchable' module Searchable extend ActiveSupport::Concern module ClassMethods def...

Rails Association Guidance [on hold]


ruby-on-rails,ruby,ruby-on-rails-4,ruby-on-rails-3.2
I am new to rails 4. I have gone through lots of tutorials and trying to solve below scenario. But still no success. Can anybody point me in the right direction. How to handle associations for below scenario. Scenario: 1. Patient can have many surgeries. 2. Surgery has two types...

Ruby access words in string


ruby
I don't understand the best method to access a certain word by it's number in a string. I tried using [] to access a word but instead it returns letter. puts s # => I went for a walk puts s[3] # => w ...

Allowing some enabled and disabled option on collection_select


ruby-on-rails,ruby
I am trying to populate a dropdown box on a view that has all the states. This works just fine: <%= f.collection_select :state_id, @states, :id, :name %> Now, I need to make the following: Some states are going to be disabled for choosing, but they still have to appear on...

Appending an element to a page in VoltRb


html,ruby,opalrb,voltrb
I'm trying to append an element to one of my pages in a Volt project, via opal-browser, like so: if RUBY_PLATFORM == 'opal' require 'browser' $document.body << my_dom_element.to_n end # controller code below Unfortunately, I'm getting an error: [Error] TypeError: null is not an object (evaluating 'value.nodeType') (anonymous function) (main.js,...

Ruby gsub group parameters do not work when preceded by escaped slashes


ruby,regex
I am trying to perform a trivial substitution, that in any other language I have come across, work as per the documentation. However, my substitution fails for some reason. The documentation examples list: "hello".gsub(/[aeiou]/, '*') #=> "h*ll*" "hello".gsub(/([aeiou])/, '<\1>') #=> "h<e>ll<o>" "hello".gsub(/./) {|s| s.ord.to_s + ' '} #=> "104 101...

How could I padding spaces to a fix length


ruby
I need all strings' length with 5 Original [477, 4770,] Expected ["477 ", "4770 "] How could I do it with Ruby ?...

Ruby on Rails - Help Adding Badges to Application


ruby-on-rails,ruby,rest,activerecord,one-to-many
I'm creating a rails application that is a backend for a mobile application. The backend is implemented with a RESTful web API. Currently I am trying to add gamification to the platform through the use of badges that can be earned by the user. Right now the badges are tied...

Trying to dynamically create divs that can also be closed


javascript,html,dynamic
I'm trying to create a simple note taking application. Right now I am trying figure out how to dynamically create and delete DIV elements. I can create DIVs, but each time I add a new div it adds a "Close" "X" to each previous div. So my question is how...

Loop until i get correct user


ruby,redis
I have users stored in Redis and want to be able to call only certain subsets from a set, if i don't get the correct user back i want to put it back in the set and then try again until i get one of the desired users @redis =...

regex to pull in number with decimal or comma


ruby,regex
This is my line of code: col_value = line_item[column].scan(/\d+./).join().to_i When I enter 30,000 into the textfield, col_value is 30. I want it to bring in any number: 30,000 30.5 30.55 30000 Any of these are valid... Is there a problem with the scan and or join which would cause it...

How to alter dynamic/live element attributes - (replaceWith()) used


jquery,dynamic,replace,live
I am having a hard time wrapping my head around how to access and alter attributes of HTML content added in place of a placeholder element using replaceWith. Here is the scenario: Here is the HTML (kept in a php file) that replaces the '#null_item' element in the subsequent HTML...

is there an equivalent of the ruby any method in javascript?


javascript,arrays,ruby,iteration
Is there an equivalent of ruby's any method for arrays but in javascript? I'm looking for something like this: arr = ['foo','bar','fizz', 'buzz'] arr.any? { |w| w.include? 'z' } #=> true I can get a similar effect with javascript's forEach method but it requires iterating through the entire array rather...

Seeding fails validation for nested tables (validates_presence_of)


ruby-on-rails,ruby,validation,ruby-on-rails-4,associations
An Organization model has a 1:many association with a User model. I have the following validation in my User model file: belongs_to :organization validates_presence_of :organization_id, :unless => 'usertype==1' If usertype is 1, it means the user will have no organization associated to it. For a different usertype the presence of...

Ruby- get a xml node value


ruby,xml
can someone help me in extracting the node value for the element "Name". Type 1: I am able to extract the "name" value for the below xml by using the below code <Element> <Details> <ID>20367</ID> <Name>Ram</Name> <Name>Sam</Name> </Details> </Element> doc = Nokogiri::XML(response.body) values = doc.xpath('//Name').map{ |node| node.text}.join ',' puts values...

Stack level too deep because recursion


ruby-on-rails,ruby,ruby-on-rails-4,twitter
I have a model named Tweet. The columns of the Tweet model are: -id -content -user_id -picture -group -original_tweet_id Every tweet can have one or multiple retweets. The relation happens with the help of original_tweet_id. All the tweets have original_tweet_id nil , whilst the retweets contain the id of the...

Heroku RAM not increasing with upgraded dynos


ruby-on-rails,ruby,ruby-on-rails-3,memory,heroku
I have a massive function i have been calling manually through the heroku rails console. I have been receiving the error rapid fire in my logs: 2015-06-22T14:56:42.940517+00:00 heroku[run.9877]: Process running mem=575M(112.4%) 2015-06-22T14:56:42.940517+00:00 heroku[run.9877]: Error R14 (Memory quota exceeded) A 1X dyno is suppose to have 512 MB of RAM. I...

rails - NameError (undefined local variable or method while using has_many :through


ruby-on-rails,ruby,ruby-on-rails-4
My rails app gives following error: NameError (undefined local variable or method 'fac_allocs' for #): app/models/room.rb:4:in '' app/models/room.rb:1:in '' app/controllers/rooms_controller.rb:3:in 'index' room.rb file class Room < ActiveRecord::Base has_many :bookings has_many :fac_allocs has_many :facs, :through => fac_allocs end ...

Ruby boolean logic: some amount of variables are true


ruby
Let say I have 3 variables: a, b, c. How can I check that just zero or one of them is true?...

Using Ruby Pathname to access relative directory


ruby,path,pathname
Given I have a relative path pointing to a directory how can I use it with Ruby's Pathname or File library to get the directory itself? p = Pathname.new('dir/') p.dirname => . p.directory? => false I have tried './dir/', 'dir/', 'dir'. What I want is p.dirname to return 'dir'. I...

How to flatten a structure of embedded Set and Hash


ruby,recursion
I would like to convert an embedding structure into a flat one. An embedding structure is a set of 0 or more objects, such as: a string or a hash having some string as key and some other embedding structure as value. A flat structure is a set of arrays...

Get X days out of an Array


ruby,ruby-on-rails-4
I have an array filled with Datetime objects: [Mon, 22 Jun 2015, Tue, 23 Jun 2015, Wed, 24 Jun 2015, Thu, 25 Jun 2015, Fri, 26 Jun 2015, Sat, 27 Jun 2015, Sun, 28 Jun 2015] I know how to select what I want from the array ex: week.select{|x|x.monday? ||...

Rails basic auth not working properly


ruby-on-rails,ruby,authentication
I am building a small API that uses basic authentication. What I have done, is that a user can generate a username and password, that could be used to authenticate to the API. However I have discovered that it is not working 100% as intended. It appears that a request...

Rails - link_to path based on object's name + refactoring multiple custom actions


ruby-on-rails,ruby,refactoring
I'm looking to simplify the link_to path based on thr object's name and also am looking into refactoring multiple custom actions. I've managed to get this working below. <% ServiceMenu.all.each do |menu| %> <tr class=" <%= cycle('odd', 'even') %>"> <td><%= link_to menu.name, ("tech/""#{menu.name.parameterize}") %></td> </tr> <% end %> I feel...

Set value for Spinner with custom Adapter in Android


android,dynamic,android-arrayadapter,android-spinner
I am developing a android application with spinner in a form. The spinner items and spinner values are different. I want to collect all the value from the from including spinner and set a rest api service to web back end. here is the response array. {"Status":true,"errorType":null,"countryList":[{"Code":"US","Name":"United States"},{"Code":"CA","Name":"Canada"},{"Code":"AU","Name":"Australia"},{"Code":"GB","Name":"United Kingdom"}]} I...

How to handle backslash “\” escape characters in q string and heredocument


ruby
Ruby Newbie here. I do not understand why Ruby looks inside %q and escapes the \. I am using Ruby to generate Latex code. I need to generate \\\hline which is used in Latex for table making. I found \\\hline as input generated \hline even though the string was inside...

Can't map a range of dates in Ruby/Rails


ruby-on-rails,ruby
I'm trying to map a range of dates and pass them to my view as an array, as follows: from, to = Date.parse("2014-01-01"), Date.yesterday date_range = (from..to) @mapped_dates = date_range.map {|date| date.strftime("%b %e")} I reference them in some JS in my view as follows: dateLabels = <%= raw @mapped_dates.to_json %>;...

Same enum values for multiple columns


ruby-on-rails,ruby,enums
I need to do something like this: class PlanetEdge < ActiveRecord::Base enum :first_planet [ :earth, :mars, :jupiter] enum :second_planet [ :earth, :mars, :jupiter] end Where my table is a table of edges but each vertex is an integer. However, it seems the abvove is not possible in rails. What might...

Split an array into slices, with groupings


arrays,ruby,enumerable
I've got some Ruby code here, that works, but I'm certain I'm not doing it as efficiently as I can. I have an Array of Objects, along this line: [ { name: "foo1", location: "new york" }, { name: "foo2", location: "new york" }, { name: "foo3", location: "new york"...

Saying there are 0 arguments when I have 2? Trying to open a CSV file to write into


ruby,file,csv,dir
I'm trying to read from a CSV file and codify people into groups using an equation. I append the name of their group they fall into to the end of the array that their row creates. Then I write it to a new file so I don't overwrite the original...

How to get return value from a forked / spawned process in Ruby?


ruby,process,output,fork,spawn
My simple test program: pid = Process.spawn("sleep 10;date") How can I place the output (eg stdout) of the "date" command in a variable when it is available? I don't want to use a file for the data exchange....

Keep leading zeroes when converting string to integer


ruby
For no particular reason, I am trying to add a #reverse method to the Integer class: class Integer def reverse self.to_s.reverse.to_i end end puts 1337.reverse # => 7331 puts 1000.reverse # => 1 This works fine except for numbers ending in a 0, as shown when 1000.reverse returns 1 rather...

On rendering from controller, current_page method does not seem to work


ruby-on-rails,ruby,ruby-on-rails-4,model-view-controller
I have a navigation bar included in application.html.erb. Because for some pages, such as the signup page, I need to place additional code inside the navigation bar, I have excluded those pages for showing the navigation bar through application.html.erb and instead included it in their respective view pages. See code...

Error when trying to install app with mysql2 gem


mysql,ruby-on-rails,ruby,ruby-on-rails-4
Im trying to install an open source rails 3.2.21 application that uses the mysql2 gem, but when i try and run the bundle commant I get the following error: Fetching: mysql2-0.3.18.gem (100%) Building native extensions. This could take a while... p ERROR: Error installing mysql2: ERROR: Failed to build gem...

Ruby: How to copy the multidimensional array in new array?


ruby-on-rails,arrays,ruby,multidimensional-array
seating_arrangement [ [:first, :second, :none], [:first, :none, :second], [:second, :second, :first], ] I need to copy this array into new array. I tried to do it by following code: class Simulator @@current_state def initialize(seating_arrangement) @@current_state = seating_arrangement.dup end But whenever I am making any changes to seating_arrangement current_state changes automatically....

Heroku rake db:migrate failing - uninitialized constant


ruby-on-rails,ruby,heroku
My app is working fine locally and my push to Heroku was successful. But, when I run heroku run rake db:migrate, I get the following error: NameError: uninitialized constant AddWeightToExercises Here is the failed migration: class AddWeightToExercise < ActiveRecord::Migration def change add_column :exercises, :weight, :float end end edit: Thanks for...

.split and regular expression in Ruby


ruby,regex
I want to split my string by (',') but ignore ',' if they are inside quotes. For example " 2,2,4,'hello', 'world', 'hi, there' " I want to split it so 'hi, there' will not be split into two different array elements in ruby. How can I do that? Probably use...

RSpec test for rake task


ruby-on-rails,ruby,rspec,rake-task
I have created a custom rake task that deletes all items that are >= 7 days old. I am trying to write a RSpec test for this new task but it seems like my task isn't really running in the test. I have tested the task manually and it works...

String#scan not capturing all occurences


ruby,regex
I'm facing a very strange behaviour with ruby String#scan method return. I have this code below and I can't find out why "scan" doesn't return 2 elements. str = "10011011001" regexp = "0110" p str.scan(/(#{regexp})/) ==> [["0110"]] String "str" clearly contains 2 occurences of pattern "0110". I want to fetch...

For loop with flexible stop variable


ruby
I need to write a loop for x in (1..y) where the y variable can be changed somehow. How can I do that? For example: for x in (1..y/x) But it does not work. ...

edit first line of multiple files in place with a ruby one-liner


ruby
I can edit multiple files in place using the -p -i -e command line options: ruby -pi -e 'sub(/a/,"b")' file* How can I edit only the first line of each file? This works for one file only: ruby -pi -e 'sub(/^/,"New line goes at top\n") if $. == 1' file1...

Iterating over EncryptedDataBagItem in Chef Recipe


ruby,json,chef,devops
I would like to decrypt a chef data bag item (named passwords) and store all of its attributes in a temporary JSON file which is read (and then deleted) by a node.js app. Is there a way to iterate over attributes of a data bag ITEM and get their values?...

What is Rack::Utils.multipart_part_limit within Rails and what function does it perform?


ruby-on-rails,ruby,rack,multipart
Rack::Utils.multipart_part_limit is set to 128 by default. What purpose does the value have and what effect does it have within the Rails system?...

In Ruby how to put multiple lines in one guard clause?


ruby-on-rails,ruby
I have the following line of code : if params[:"available_#{district.id}"] == 'true' @deliverycharge = @product.deliverycharges.create!(districtrate_id: district.id) delivery_custom_price(district) end Rubocop highlight it and asks me to use a guard clause for it. How can I do it? EDIT : Rubocop highlighted the first line and gave this message Use a guard...