I need to monkey-patch File. Timecop doesn't affect the time that the file system reports, which is what File.atime uses and in turn that's what HttpClient uses when posting a file to a server, which in turn means VCR doesn't quite work as desired. AFAIK, this means I can't use refinements.
I don't understand what's going on here:
class File
def atime
"this one happens"
end
end
module CoreExtensions
module File
module TimecopCompat
def atime
"this one does not"
end
end
end
end
File.include CoreExtensions::File::TimecopCompat
File.new('somefile').atime # --> "this one happens"
Why does the module-based monkey patching not happen? What do I need to change for it to work? Is there a different kind of monkey-patching that I should be using?
The issue is related to the way include appends the module to the ancestor chain. "Ruby modules: Include vs Prepend vs Extend" provides a very detailed overview of the differences between include and prepend.
Take a look at these two examples:
class Foo
def hello
"1"
end
end
module Bar
def hello
"2"
end
end
Foo.include Bar
Foo.new.hello
# => "1"
Foo.ancestors
# => [Foo, Bar, Object, Kernel, BasicObject]
versus
class Foo
def hello
"1"
end
end
module Bar
def hello
"2"
end
end
Foo.prepend Bar
Foo.new.hello
# => "2"
Foo.ancestors
# => [Bar, Foo, Object, Kernel, BasicObject]
Basically, you want to use prepend in your case as include won't override the existing method.
include is not some magic thing. It is very simple actually: it makes the module the superclass of the class it is mixed into. Now: do superclass methods override subclass methods? No, of course not, it is the other way around.
Therefore, include cannot possibly override methods of the class the module is being included into.
That is what prepend is for, which mixes in a module at the beginning of the ancestors hierarchy. (Which unfortunately cannot be simply explained using inheritance, it is something different.)
Let's simplify your example without changing the question.
module TimecopCompat
def atime
"this one does not"
end
end
I have left the class File alone as it already has an instance method File#atime.
File.new('temp').atime
#=> 2019-07-16 20:20:51 -0700
As explained by the other answers, executing
File.include TimecopCompat
results in:
File.ancestors
#=> [File, TimecopCompat, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
File.new('temp').atime
#=> 2019-07-16 20:20:51 -0700
whereas executing
File.prepend TimecopCompat
results in:
File.ancestors
#=> [TimecopCompat, File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
File.new('temp').atime
#=> "this one does not"
It is poor practice, however, to change the behavior of any core method, as its original behavior may be relied upon elsewhere in the program.
Here there are two acceptable practices. The first is to create a method (new_atime, say) that has a File object (file, say) as its argument:
file = File.new('temp')
x = new_atime(file)
new_atime cannot be chained with a File object as its receiver, but that is a small price to pay for a safe and robust solution.
The second option is to use Refinements to refine the File class.
module RefinedFile
refine File do
def atime
"this one does not"
end
end
end
class C
using RefinedFile
File.new('temp').atime
end
#=> "this one does not"
We can confirm that File#atime has not been altered outside the class C:
File.new('temp').atime
#=> 2019-07-16 20:20:51 -0700
Related
Having reading through The Ruby programming language I found an example of using alias keyword for augmenting the methods.
def hello # A nice simple method
puts 'Hello world' # Suppose we want to augment it...
end
alias original_hello hello # Give the method a backup name
def hello # Now we define a new method with the old name
puts "Your attention please" # That does some stuff
original_hello # Then calls the original method
puts "This has been a test" # Then does some more stuff
end
Indeed original hello preserves the old behavior even after the method the it had been referencing to was redefined.
But, to my mind, this example hardly clarifies the real benefit of this technique. Cannot the same be achieved in traditional way (e.g. by providing the block)? Then why applying this idiom? Can anyone provide an example from the real world when augmenting with alias really makes sense?
Rails code is full of those. Imagine the original hello method does not belong to your code base. Somewhere in 3rd-party library there is do_stuff(stuff) method declared on the class Stuffer.
You want to e.g. debug this method. You reopen the class, define an alias and, voilà:
class Stuffer
alias original_do_stuff do_stuff
def do_stuff(stuff)
puts stuff.inspect
original_do_stuff(stuff)
end
end
Now all the code, including original 3rd party code you might be even not aware about, would print out the parameter passed to every single call to do_stuff.
Real-life example (don’t try this at home and in the school :)
class String
alias _inspect inspect
def inspect
puts "I am a string: “#{_inspect}”"
end
end
"abc".inspect
#⇒ I am a string: “"abc"”
Can anyone provide an example from the real world when augmenting with alias really makes sense?
Not really. Today, you would do this (example taken from #mudasobwa's answer):
module WeirdInspectRefinement
module WeirdInspectExtension
def inspect
"I am a string: “#{super}”"
end
end
refine String do
prepend WeirdInspectExtension
end
end
using WeirdInspectRefinement
p 'abc'.inspect
#⇒ 'I am a string: “"abc"”'
But even before Module#prepend and Refinements existed, there was never a reason to use alias for this, which leaves unused methods around polluting the namespace, and Rails abandoned it quite a while ago:
class String
old_inspect = instance_method(:inspect)
define_method(:inspect) do
"I am a string: “#{old_inspect.bind(self).()}”"
end
end
'abc'.inspect
#⇒ 'I am a string: “"abc"”'
I have following file
lib/a/b/c.rb
class a::b::c
def request(env)
#some code here
end
end
Now i am using rubocop style
Style/ClassAndModuleChildren:
Enabled: true
I am getting rubocop offense for this
lib/a/b/c.rb:1:7: C: Use nested module/class definitions instead of compact style.
class a::b::c
When i update my code to following offence get fixed
Style 1
class a
class b
class c
def request(env)
#some code here
end
end
end
end
Style 2
module a
module b
class c
def request(env)
#some code here
end
end
end
end
I think i should use Style 2 as i am using require 'a' in one of my file.
Please let me know how to fix this type & offences and reason for it
The reason this is marked as offence is that constants resolution works lexically, rather than semantically in ruby. This is not intuitive and might lead to some obscure errors (for example if you have two classes with the same name in two different scopes). Compare these two cases:
# Given
module Foo
X = 42
end
# This
module Foo
class Bar
def baz
puts X
end
end
end
# VS
class Foo::Bar
def baz
puts X
end
end
Now when you call:
Foo::Bar.new.baz
In the first case you will get 42, in the second - NameError: uninitialized constant Foo::Bar::X
As for which is the correct way to fix it: Note that using the short syntax will give you an error if Foo doesn't already exist. The answer is - you should use whatever Foo is. If it's a class - use class, if it's a module - module.
Class class is essentially derived from Module class, that’s why Class is a Module with some added functionality; basically the difference is in that Classes might be instantiated.
So, the answer to your question would be: use whatever is more suitable in your case (and by the information given it is impossible to say, what it is.)
require 'a' has nothing to do with the case, since require directive just forces ruby interpreter to load the respective code.
NB: that kind of question is the exact reason why Rubocop complains: it prevents you from using something you are not certain about and forces you to understand, is it in fact Module, or Class by it’s nature.
BTW, since you mentioned you have require 'a' somewhere, it probably means that in this a.rb you have already either module A or class A.
BTW#2 Both class and module names must begin with capital letter, because they are to be constants.
The issue I am faced with is that I need to prevent a Ruby class from being manipulated after it is defined. I can freeze it, but that doesn't stop people from just overwriting it all together.
I realize that some will want to respond with some sort of "Ruby isn't meant to be used like this" mantra. I get it, but my case is very special. This is for codewars.com where user submitted solutions are combined with a custom test framework, so I need to stop the user-submitted code from tinkering with the Test class.
I had thought that it wasn't possible at all to make constants true constants, but I noticed that the $? global variable is like this. Its likely that its because its built-in to the language to be like this and not something that can be done with custom variables.
That's because it is built into the language.
In Ruby there is no way to truly define a constant. The closest you can come is writing custom getters/setters and throwing an error if a variable has already been set.
Throw exception when re-assigning a constant in Ruby?
This is defined as "The status of the last executed child process." so, if you assign something to that variable, it will be immediately overwritten by the language with the result of the last (your) assignation.
I would think about a custom implementation - perhaps extracted into some helper gem?
def foo
#foo
end
def foo=(foo)
if defined?(#foo)
warn "warning: already initialized foo"
else
#foo = foo
end
end
self.foo = :bar
puts foo # => bar
self.foo = :baz # => warning: already initialized foo
puts foo # => bar
(StackOverflow is telling me that this question is "subjective and likely to be closed"… well, I'll give it a shot regardless)
I'm writing a bunch of helper methods (for a TextMate bundle), and I'd like (and I need) to have them neatly namespaced.
These methods are really just functions, i.e. they don't operate on anything outside their own scope, and thus don't really belong in a class. There's nothing that needs instantiating.
So far, I've been doing this and that works just fine
module Helpers::Foo
module_function
def bar
# ...
end
end
Helpers::Foo.bar # this is how I'd like to call the method/function
But would it be better to:
1. Skip module_function and declare the methods/functions as self.*?
2. Or would it be better to declare a class instead of a module?
3. Or use class << self (inside a module or a class)?
4. Or something else entirely?
I realize this is a pretty open-ended question, but I'm really just looking to hear what people are doing.
I prefer either
module Foo
def self.bar
"bar"
end
end
Foo.bar #=> "bar"
or
module Foo
def Foo.bar
"bar"
end
end
Foo.bar #=> "bar"
but probably lean towards the former, i think self. is really descriptive.
Edit: After reading the comments I propose a third option that I prefer for readability. Technically I think this would be defined as extending the methods included on the Eigen class.
module Foo
module ClassMethods
def baz
"baz"
end
end
extend ClassMethods
end
Foo.baz #=> "baz"
Often within the console, I'll interrogate an object
pp obj.methods.sort #or...
pp (obj.methods - Object.methods).sort
In Ruby it's pretty common for a developer to provide aliases for methods. I am wondering if there is a reflective way of identifying aliases so that I might be able to display aliased methods, something like...
array.aliased_methods #=> {:collect => :map, ...}
This would be helpful for being able to identify exactly how many things an object can do.
In Ruby 1.9, aliased instance methods will be eql?, so you can define:
class Module
def aliased_methods
instance_methods.group_by{|m| instance_method(m)}.
map(&:last).keep_if{|symbols| symbols.length > 1}
end
end
Now if you try it, you will get:
class Foo
def bar; 42 end
alias baz bar
def hello; 42 end
end
Foo.aliased_methods # => [[:bar, :baz]]
Array.aliased_methods # => [[:inspect, :to_s], [:length, :size]]
Note that some pairs are missing, e.g. [:map, :collect]. This is due to a bug that is now fixed and will be in the next version (2.0.0) If it is important to you, you can roll your own group_by without using hashes or eql? and only using ==.
Not really. Alias isn't just a pointer or something like that, after an alias you can undef the first method and the aliased method won't change (think hard link vs sym link). Typically, aliases are reflected in the rdoc, so I would go there for a definitive list.