conditional parameters in ruby, how to check for nil? - ruby-on-rails

Tried the following
def pkill(process_name, *host)
puts host.nil? # => false
puts host.empty? # => true
puts host # => nil
puts host[0].nil? # => true
end
Now if I call send("pkill", blah, nil), how/why is host false??
Pretty new to ruby, so keep it smooth :)

Because it's not nil, it's empty array.
def pkill(process_name, *host)
host.nil? # => false
host # => []
host.empty? # => true
end
pkill 'blah'

Are you just trying to make the host parameter optional? The intended use of *host is if you have a variable number of parameters. For example, you method could be called like pkill('blah'), or pkill('blah', 'bloo') or pkill('blah', 'bloo', 'bar'), etc.
If you are just trying to say that host isn't a required argument, you should give it a default value. For example, your method could become
def pkill(process_name, host=nil)
puts host.nil?
puts host
end

Related

Minitest::Mock#expect without specific order?

Suppose I have a class:
class Foo
def process
MyModel.where(id: [1,3,5,7]).each do |my_model|
ExternalService.dispatch(my_modal.id)
end
end
end
I want to test it:
class FooTest < ActiveSupport::TestCase
def process_test
external_service_mock = MiniTest::Mock.new
[1,3,5,7].each do |id|
external_service_mock.expect(:call, true, id)
end
ExternalService.stub(:dispatch, events_mock) do
Foo.new.process
end
external_service_mock.verify
end
end
However, #expect enforces that the following calls are made in the same order as #expect was called. That's not good for me, because I have no confidence, in what order will the results be returned by the DB.
How can I solve this problem? Is there a way to expect calls without specific order?
Try Using a Set
require 'set'
xs = Set[3,5,7,9]
#cat = Minitest::Mock.new
#cat.expect :meow?, true, [xs]
#cat.meow? 7 # => ok...
#cat.expect :meow?, true, [xs]
#cat.meow? 4 # => boom!
Alternatively, a less specific option:
Given that the value returned by the mock isn't a function of the parameter value, perhaps you can just specify a class for the parameter when setting up your mock. Here's an example of a cat that expects meow? to be called four times with an arbitrary integer.
#cat = Minitest::Mock.new
4.times { #cat.expect(:meow?, true, [Integer]) }
# Yep, I can meow thrice.
#cat.meow? 3 # => true
# Mope, I can't meow a potato number of times.
#cat.meow? "potato" # => MockExpectationError

How to track objects "called" inside a block?

Question:
I need to know the records' attributes that have been called inside a block (say I need something like the following):
def my_custom_method(&block)
some_method_that_starts_tracking
block.call
some_method_that_stops_tracking
puts some_method_that_returns_called_records_attributes
do_something_about(some_method_that_returns_called_records_attributes)
end
my_custom_method { somecodethatcallsauthorofbook1andemailandfirstnameofuser43 }
# this is the `puts` output above (just as an example)
# => {
# #<Book id:1...> => [:author],
# #<User id:43...> => [:email, :first_name]
# }
code inside the block can be anything
Specifically, I meant to track any instance of a subclass of ApplicationRecord, so it can be instance of any models like Book, User, etc...
Attempts:
From my understanding, this is similar to how rspec works when a method is expected to be called. That it somehow tracks any calls of that method. So, my initial attempt is to do something like the following (which does not yet fully work):
def my_custom_method(&block)
called_records_attributes = {}
ApplicationRecord.descendants.each do |klass|
klass.class_eval do
attribute_names.each do |attribute_name|
define_method(attribute_name) do
called_records_attributes[self] ||= []
called_records_attributes[self] << attribute_name
self[attribute_name]
end
end
end
end
block.call
# the above code will work but at this point, I don't know how to clean the methods that were defined above, as the above define_methods should only be temporary
puts called_records_attributes
end
my_custom_method { Book.find_by(id: 1).title }
# => {
# #<Book id: 1...> => ['title']
# }
the .descendants above probably is not a good idea because Rails use autoload if I'm not mistaken
as already said above in the comment, I do not know how to remove these "defined_methods" that are just supposed to be only temporary for the duration of this "block".
furthermore, my code above would probably have overriden the "actual" attribute getters of the models, if ever any has been already defined, which is bad.
Background:
I am writing a gem live_record which I am adding a new feature that will allow a developer to just simply write something like
<!-- app/views/application.html.erb -->
<body>
<%= live_record_sync { #book.some_custom_method_about_book } %>
</body>
... which will render #book.some_custom_method_about_book as-is on the page, but at the same time the live_record_sync wrapper method would take note of all the attributes that have been called inside that block (i.e. inside some_custom_method_about_book the #book.title is called), and then it sets these attributes as the block's own "dependencies", in which later when that specific book's attribute has been updated, I can already also update directly the HTML page of which this attribute is a "dependency" as like specified just above. I am aware that this is not an accurate solution, but I'd like to open up my chances by experimenting on this first.
-- Rails 5
Disclaimer: I believe this is just a mediocre solution, but hopefully helps anyone with the same problem.
I tried reading rspec source code, but because I couldn't easily comprehend what is happening under the hood, and that it occurred to me that rspec's (i.e.) expect(Book.first).to receive(:title) is different from what I really want because the methods there are already specified (i.e. :title), while what I want is to track ANY methods that are attributes, so because of these two reasons I skipped reading further, and attempted my own solution, which hopefully did somehow work; see below.
Note that I am using Thread local-storage here, so this code should be thread-safe (untested yet).
# lib/my_tracker.rb
class MyTracker
Thread.current[:my_tracker_current_tracked_records] = {}
attr_accessor :tracked_records
class << self
def add_to_tracked_records(record, attribute_name)
Thread.current[:my_tracker_current_tracked_records][{model: record.class.name.to_sym, record_id: record.id}] ||= []
Thread.current[:my_tracker_current_tracked_records][{model: record.class.name.to_sym, record_id: record.id}] << attribute_name
end
end
def initialize(block)
#block = block
end
def call_block_while_tracking_records
start_tracking
#block_evaluated_value = #block.call
#tracked_records = Thread.current[:my_tracker_current_tracked_records]
stop_tracking
end
def to_s
#block_evaluated_value
end
# because I am tracking record-attributes, and you might want to track a different object / method, then you'll need to write your own `prepend` extension (look for how to use `prepend` in ruby)
module ActiveRecordExtensions
def _read_attribute(attribute_name)
if Thread.current[:my_tracker_current_tracked_records] && !Thread.current[:my_tracker_is_tracking_locked] && self.class < ApplicationRecord
# I added this "lock" to prevent infinite loop inside `add_to_tracked_records` as I am calling the record.id there, which is then calling this _read_attribute, and then loops.
Thread.current[:my_tracker_is_tracking_locked] = true
::MyTracker.add_to_tracked_records(self, attribute_name)
Thread.current[:my_tracker_is_tracking_locked] = false
end
super(attribute_name)
end
end
module Helpers
def track_records(&block)
my_tracker = MyTracker.new(block)
my_tracker.call_block_while_tracking_records
my_tracker
end
end
private
def start_tracking
Thread.current[:my_tracker_current_tracked_records] = {}
end
def stop_tracking
Thread.current[:my_tracker_current_tracked_records] = nil
end
end
ActiveSupport.on_load(:active_record) do
prepend MyTracker::ActiveRecordExtensions
end
ActiveSupport.on_load(:action_view) do
include MyTracker::Helpers
end
ActiveSupport.on_load(:action_controller) do
include MyTracker::Helpers
end
Usage Example
some_controller.rb
book = Book.find_by(id: 1)
user = User.find_by(id: 43)
my_tracker = track_records do
book.title
if user.created_at == book.created_at
puts 'same date'
end
'thisisthelastlineofthisblockandthereforewillbereturned'
end
puts my_tracker.class
# => #<MyTracker ... >
puts my_tracker.tracked_records
# => {
# {model: :Book, record_id: 1} => ['title', 'created_at'],
# {model: :User, record_id: 43} => ['created_at']
# }
puts my_tracker
# => 'thisisthelastlineofthisblockandthereforewillbereturned'
# notice that `puts my_tracker` above prints out the block itself
# this is because I defined `.to_s` above.
# I need this `.to_s` so I can immediately print the block as-is in the views.
# see example below
some_view.html.erb
<%= track_records { current_user.email } %>
P.S. Maybe it's better that I wrap this up as a gem. If you're interested, let me know

Rails/Ruby incorrectly showing variable not defined

In debugging console, while app running (using binding.pry to interrupt it), I can see that my variable Rails.configuration.hardcoded_current_user_key is set:
pry(#<TasksController>)> Rails.configuration.hardcoded_current_user_key
=> "dev"
But it doesn't appear to be defined:
pry(#<TasksController>)> defined?(Rails.configuration.hardcoded_current_user_key)
=> nil
Yet it works fine to store and test its value:
pry(#<TasksController>)> tempVar = Rails.configuration.hardcoded_current_user_key
=> "dev"
pry(#<TasksController>)> defined?(tempVar)
=> "local-variable"
What is going on?
This is because Rails config implements respond_to? but not respond_to_missing?, and defined? only recognizes respond_to_missing?:
class X
def respond_to?(name, include_all = false)
name == :another_secret || super
end
private
def method_missing(name, *args, &block)
case name
when :super_secret
'Bingo!'
when :another_secret
'Nope.'
else
super
end
end
def respond_to_missing?(name, include_all = false)
name == :super_secret || super
end
end
x = X.new
puts x.super_secret # => Bingo!
p defined?(x.super_secret) # => "method"
puts x.another_secret # => Nope.
p defined?(x.another_secret) # => nil
It's recommended to implement respond_to_missing? along with method_missing, I too wonder why Rails did it that way.
You shouldn't be using defined? on anything but the "stub" of that, or in other words, merely this:
defined?(Rails)
Anything beyond that is highly unusual to see, and I'm not even sure it's valid.
defined? is not a method, but a construct that tests if the following thing is defined as a variable, constant or method, among other things. It won't evaluate your code, it will just test it as-is. This means method calls don't happen, and as such, can't be chained.
If you want to test that something is assigned, then you should use this:
Rails.configuration.hardcoded_current_user_key.nil?

How to pass Arguments and use those in (resque-status) Resque::JobWithStatus?

my resque worker class is:
require 'resque'
require 'resque/job_with_status'
class PatstatResqueWorker < Resque::JobWithStatus
#queue = :my_worker_q
def self.perform(query, label)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
end
and my controller part, where I call this resque is...
class MyController < ApplicationController
def resque
job_id = PatstatResqueWorker.create(:query => #query, :label => "yes")
status = Resque::Plugins::Status::Hash.get(job_id)
end
end
and its not working :(
if i remove the parameter from resque function it says Wrong number of arguments (2 for 0) and if i add the parameter section back it says options not defined :(
Could you help?
The reason you're getting the "options not defined" error is that you haven't defined options in the method that uses it. Your self.perform method expects to receive two distinct arguments, query and label, but the code inside the method expects to have an options hash. You've got to choose one or the other.
Either do this:
def self.perform(query, label)
# use the parameters we've already defined
puts "query:"
puts query
puts "label:"
puts label
end
# call it like this
PatstatResqueWorker.create(#query, "yes")
Or else do this:
# change the method signature to match what you're doing
def self.perform(options)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
# call it like this, with string keys
PatstatResqueWorker.create('query' => #query, 'label' => "yes")
Notice that with the hash version, I changed the call to use strings for the hash keys instead of symbols. You can use symbols if you want, but you'd have to change it in the body of the method as well (i.e. options[:query] instead of options['query']). You've just got to be consistent.

Trying to understand what Base.rakismet_binding is for

What does this part . . .
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
of the following method do?
module InstanceMethods
def spam?
data = akismet_data
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
self.akismet_response = Rakismet::Base.akismet_call('comment-check', data)
self.akismet_response == 'true'
end
I found other references to rakismet_binding in rakismet.rb:
class Base
cattr_accessor :valid_key, :rakismet_binding
and controller_extensions.rb:
def rakismet(&block)
Rakismet::Base.rakismet_binding = binding
yield
Rakismet::Base.rakismet_binding = nil
end
private :rakismet
But I have no idea what it's for.
The Kernel binding is a special object holding the context of a method call including all instance variables.
What rakismet(&block) method does, is to temporary assign the current binding (the ActionController instance) to a class variable so it can be accessible by any rakismet method calls and execute the content of the block.
The following code fragment
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
checks whether a binding is available and if so, it tries to automatically collect some information from the current binding such as the ActionController#request.referer, the ActionController#request.remote_ip and so on.
In a few words, this is a workaround to collect some variables from your current ActionController request that otherwise won't be available to Rakismet.
The last code fragment pretty much indicates its intention - its to be used in block form and wraps the current binding.
If you look at the some unit tests for this class:
http://github.com/jfrench/rakismet/blob/master/spec/models/model_extension_spec.rb?raw=true
You can see how its used.

Resources