Aliasing a setter method - ruby

class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
def something=(a)
a += 2 # do something with the argument
old_something=(a) # then pass it on
end
end
I would expect that if I said
t = Test.new
t.something = 3
puts t.something
It would print out 5. But it prints nil. Why does this not work?

The form
foo = bar
assigns to a local variable. You need to make it explicit that you want to call a method:
self.foo = bar

Edit: #Jörg's answer is probably the real problem, but my original answer below may be helpful as well. Redacted some misleading details from my old answer.
If you drop the def something= entirely, you'll get a setter for something which has an alias of old_something:
class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
end
1.9.3p327 :001 > require "./test.rb"
=> true
1.9.3p327 :002 > t = Test.new
=> #<Test:0x000000018eb740>
1.9.3p327 :003 > t.something = "blah"
=> "blah"
1.9.3p327 :004 > t.something
=> "blah"
1.9.3p327 :005 > t.old_something = "moo"
=> "moo"
1.9.3p327 :006 > t.something
=> "moo"

The reason is that #something is never assigned to anything.
This code will do what you are trying to do:
class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
def something=(a)
#something = a += 2 # do something with the argument
old_something=(a) # then pass it on
end
end
t = Test.new
t.something=( 3)
puts t.something
The difference was assinging the instance variable #something to a value, and then allowing your code to increment it by the a variable that was passed in.

Related

Ruby keyword arguments

I have the class. Method bar should accept argument foo with default value equal to #foo
class Foo
attr_accessor :foo
def bar(foo: foo)
p foo
end
end
In irb I execute:
> f = Foo.new
> f.foo = 'foobar'
> f.bar
For ruby 2.0 result is:
=> "foobar"
and for ruby 2.1:
=> nil
Who can explain this behavior?
Further research:
# (Ruby 2.1.0)
class Foo
attr_accessor :foo
def bar(foo: self.foo)
foo
end
end
f = Foo.new
f.foo = 'bar'
f.bar
# => "bar"
It seems Ruby 2.1.0 "initializes" local variable before evaluating the "right side" of this statement, so foo on the right side is treated as local variable and thus is evaluated to nil.
This experiment seems to confirm my hypothesis:
class Foo
attr_accessor :foo
def bar(foo: defined?(foo))
foo
end
end
# Ruby 2.0.0:
Foo.new.bar
# => "method"
# Ruby 2.1.0:
Foo.new.bar
# => "local-variable"

Is it possible to mixin before the constructor is called?

I thought I'd make a mixin for logging to keep my code DRY. This is how it looks:
# Mixin that provides shortcuts for logging methods.
module Logging
def self.included(base)
base.class_exec {
#logger_name = base.to_s
#subloggers = []
#logger = Logger.new(STDOUT)
#logger.level = Logger::FATAL
}
end
def logger=(logger)
#logger = logger
#subloggers.each { |obj| obj.logger = logger }
end
def debug(&block)
#logger.debug(#logger_name, &block)
end
def info(&block)
#logger.info(#logger_name, &block)
end
def warn(&block)
#logger.warn(#logger_name, &block)
end
def error(&block)
#logger.error(#logger_name, &block)
end
def fatal(&block)
#logger.fatal(#logger_name, &block)
end
end
In theory, I should now be able to do this:
class SomeClass
include Logging
def foo_bar
debug { "foo_bar is being executed" }
fatal { "IT'S A TRAP" }
end
end
The problem is that apparently initialize is called before the mixins are included, causing this to fail:
class SomeClass
include Logging
def initialize
#cache = CacheClass.new
#subloggers << #cache # #subloggers is nil
end
end
And I can't picture any way around it. I always create my dependencies in the constructor, and I need the Logging mixin to be available at that point. Any ideas?
This is wrong:
The problem is that apparently initialize is called before the mixins are included, causing this to fail:
Contrary to what you wrote, initialize is not called. #subloggers is not initialized for the SomeClass instance. Are you thinking that #subloggers assigned in Logging.included(base) is the same variable as the #subloggers you are accessing from the SomeClass constructor? That should not be the case. An instance variable cannot be created before creation of the instance itself.
I think when you mix in that class, those variables become instance variables on the class. Not to be confused with class variables, or regular instance variables. Check out this little irb conversation:
1.9.3p194 :012 > class Foo
1.9.3p194 :013?> def self.bar
1.9.3p194 :014?> #bar
1.9.3p194 :015?> end
1.9.3p194 :016?> def self.bar=(x)
1.9.3p194 :017?> #bar=x
1.9.3p194 :018?> end
1.9.3p194 :019?> def self.show
1.9.3p194 :020?> puts #bar
1.9.3p194 :021?> end
1.9.3p194 :022?> end
=> nil
1.9.3p194 :023 > Foo.bar=5
=> 5
1.9.3p194 :024 > Foo.bar
=> 5
1.9.3p194 :025 > Foo.show
5
=> nil
1.9.3p194 :026 > a=Foo.new
=> #<Foo:0x007fe9038e5dc0>
1.9.3p194 :029 > a.instance_variables
=> []
1.9.3p194 :030 > a.class.instance_variables
=> [:#bar]
Compare the results of your classes:
SomeClass.instance_variables
SomeClass.new.instance_variables
SomeClass.new.class.instance_variables

Extending a ruby eigenclass to load CarrierWave

Update: I've simplified my question; you can see the full history by checking out my editing revisions. Thanks to iain and bernardk for getting me this far.
I want to load carrierwave functionality into an instance of my User < ActiveRecord::Base model.
require 'uploaders/avatar_uploader'
module HasAnAvatar
def self.extended(host)
if host.class == Class
mount_uploader :avatar, AvatarUploader
else
class << host
mount_uploader :avatar, AvatarUploader
end
end
end
end
Executing:
(user = User.first).extend(HasAnAvatar).avatar
Results in:
NoMethodError: undefined method new' for nil:NilClass from
/Users/evan/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/carrierwave-0.6.2/lib/carrierwave/mount.rb:306:in
uploader'
I suspect the problem is that mount_uploader in HasAnAvatar is not being invoked properly on the eigenclass for user, such that the uploaders hash isn't populated.
Any thoughts on how to get this working?
Here is an example Rails app for this issue: https://github.com/neezer/extend_with_avatar_example
Here are two ways you can can include a module into an instance (which is to basically extend the instance's eigenclass). I don't think this is the best answer to your problem though, even if it may answer the question (in part).
class A
end
# => nil
module B
def blah
"Blah!"
end
end
# => nil
a = A.new
=> #<A:0x0000010086cdf0>
a.blah
# NoMethodError: undefined method `blah' for #<A:0x0000010086cdf0>
class << a
include B
end
a.blah
# => "Blah!"
b = A.new
# => #<A:0x0000010083b818>
b.blah
# NoMethodError: undefined method `blah' for #<A:0x0000010083b818>
b.extend B
# => #<A:0x0000010083b818>
b.blah
# => "Blah!"
c.blah
# NoMethodError: undefined method `blah' for #<A:0x0000010085eed0>
From your edit:
module Pacifiable
def pacified_with(mechanism)
class_eval do
define_method(:"pacified_with_#{mechanism}?") { true }
end
end
end
# => nil
class JellyFish
define_method(:is_squishy?) { true }
end
# => #<Proc:0x00000100850448#(irb):10 (lambda)>
class Lobster
extend Pacifiable
pacified_with :polar_bear
define_method(:is_squishy?) { false }
end
# => #<Proc:0x00000100960540#(irb):16 (lambda)>
lobster = Lobster.new
# => #<Lobster:0x0000010095aa50>
lobster.pacified_with_polar_bear?
# => true
jelly = JellyFish.new
# => #<JellyFish:0x00000100951108>
jelly.pacified_with_polar_bear?
# NoMethodError: undefined method `pacified_with_polar_bear?' for #<JellyFish:0x00000100951108>
class << jelly
extend Pacifiable
pacified_with :polar_bear
end
# => #<Proc:0x0000010093ddd8#(irb):4 (lambda)>
jelly.pacified_with_polar_bear?
# => true
big_jelly = JellyFish.new
# => #<JellyFish:0x0000010091ad38>
big_jelly.pacified_with_polar_bear?
# NoMethodError: undefined method `pacified_with_polar_bear?' for #<JellyFish:0x0000010091ad38>
From what I know of Ruby classes, once I include a module into a
class, ... but not retroactively alter any existing instances
of User.
On the contrary, an include/extend immediately affects all existing instances, because it is a question of pointer between the class and its superclass. See How does Inheritance work in Ruby? and also the links inside.
module HasAnAvatar
def m
puts 'in HasAnAvatar#m'
end
end
class AvatarUploader; end
class User
end
user = User.new
print 'user.respond_to?(:m) ? '; puts user.respond_to?(:m) ? 'yes' : 'no'
print '1) user.respond_to?(:avatar) ? '; puts user.respond_to?(:avatar) ? 'yes' : 'no'
class User
include HasAnAvatar
end
print 'user.respond_to?(:m) ? '; puts user.respond_to?(:m) ? 'yes' : 'no'
print '2) user.respond_to?(:avatar) ? '; puts user.respond_to?(:avatar) ? 'yes' : 'no'
module HasAnAvatar
def self.included(base)
puts "#{self} included into #{base}"
# base.mount_uploader :avatar, AvatarUploader
base.send(:attr_reader, :avatar)
end
end
class User
include HasAnAvatar
end
print '3) user.respond_to?(:avatar) ? '; puts user.respond_to?(:avatar) ? 'yes' : 'no'
print 'user.avatar : '; p user.avatar
print 'user.avatar.class : '; p user.avatar.class
user.instance_variable_set(:#avatar, AvatarUploader.new)
print 'after set, user.avatar : '; p user.avatar
print 'user.avatar.class : '; p user.avatar.class
Execution (Ruby 1.9.2) :
$ ruby -w t.rb
user.respond_to?(:m) ? no
1) user.respond_to?(:avatar) ? no
user.respond_to?(:m) ? yes
2) user.respond_to?(:avatar) ? no
HasAnAvatar included into User
3) user.respond_to?(:avatar) ? yes
user.avatar : nil
user.avatar.class : NilClass
after set, user.avatar : #<AvatarUploader:0x007fcc2b047cf8>
user.avatar.class : AvatarUploader
So included methods immediately become available to all existing instances.
Why does user.avatar answer nil ? Because instance variables belong to ... single instances. See Why are symbols in Ruby not thought of as a type of variable?
In this case, the old user was not assigned an avatar.
But why do you get user2.avatar.class #=> AvatarUploader. I suppose that, behind the scene, base.mount_uploader :avatar, AvatarUploader does something such that :avatar is not an accessor of a corresponding instance variable, or defines an initialize method which starts to set this variable into new instances.
Here is a solution for the second example :
module Pacifiable
def self.extended(host)
puts "#{host} extended by #{self}"
def host.pacified_with(mechanism)
##method_name = "pacified_with_#{mechanism}?"
puts "about to define '#{##method_name}' into #{self} of class #{self.class }"
if self.class == Class
then # define an instance method in a class
define_method(##method_name) { true }
else # define a singleton method for an object
class << self
define_method(##method_name) { true }
end
end
end
end
end
class JellyFish
define_method(:is_squishy?) { true }
end
class Lobster
extend Pacifiable
pacified_with :polar_bear
define_method(:is_squishy?) { false }
end
a_lobster = Lobster.new
print 'a_lobster.pacified_with_polar_bear? '; p a_lobster.pacified_with_polar_bear?
print 'a_lobster.is_squishy? '; p a_lobster.is_squishy?
jelly = JellyFish.new
### Add functionality to instance
#
## what I want:
#
jelly.extend(Pacifiable)
jelly.pacified_with(:polar_bear)
print 'jelly.pacified_with_polar_bear? '; p jelly.pacified_with_polar_bear? #=> true
Execution :
Lobster extended by Pacifiable
about to define 'pacified_with_polar_bear?' into Lobster of class Class
a_lobster.pacified_with_polar_bear? true
a_lobster.is_squishy? false
#<JellyFish:0x007fcc2b047640> extended by Pacifiable
about to define 'pacified_with_polar_bear?' into #<JellyFish:0x007fcc2b047640> of class JellyFish
jelly.pacified_with_polar_bear? true
About jelly.class.pacified_with(:polar_bear) : the mistake was to call class, which answers the class JellyFish; what you want is the singleton class of the instance object jelly. Once a method is defined in the singleton class of an object, you can send it directly to the object. The same applies to classes, which are instances of Class. Once a method is defined in the singleton class of a class, you can send it directly to the class. We call them class methods, they are actually instance methods of the singleton class of the class. Ouf !
Last OR : as explained, if you extend the class, it is valid for all existing instances. Thus you have to extend individual instances :
class JellyFromTheBigBlueSea
def find
puts 'in JellyFromTheBigBlueSea#find'
jelly = JellyFish.new
jelly.extend(Pacifiable)
jelly.pacified_with :polar_bear
jelly
end
end
class JellyFromAnIsolatedCove
def find
puts 'in JellyFromAnIsolatedCove#find'
JellyFish.new
end
end
normal_jelly = JellyFromTheBigBlueSea.new.find
ignorant_jelly = JellyFromAnIsolatedCove.new.find
## what I want:
#
print 'normal_jelly.pacified_with_polar_bear? '; p normal_jelly.pacified_with_polar_bear?
print 'ignorant_jelly.pacified_with_polar_bear?' ; p ignorant_jelly.pacified_with_polar_bear?
Execution :
in JellyFromTheBigBlueSea#find
#<JellyFish:0x007fb5d9045060> extended by Pacifiable
about to define 'pacified_with_polar_bear?' into #<JellyFish:0x007fb5d9045060> of class JellyFish
in JellyFromAnIsolatedCove#find
normal_jelly.pacified_with_polar_bear? true
ignorant_jelly.pacified_with_polar_bear?t.rb:109:in `<main>': undefined method `pacified_with_polar_bear?' for #<JellyFish:0x007fb5d9044d18> (NoMethodError)
First of all, the way with instance extend calls is working (See end of the post).
Then you should take into account some opinions that this is bad for performance
And at last, according to sources, it should work as expected. So I suggest you to wear your debug hat and try to see what exactly happens on user.extend part. For example, see if mount_uploader uses Carrierwave::Mount#mount_uploader method, as there are defined 'Uploader' everrides.
1.9.3p327 :001 > class A
1.9.3p327 :002?> def foo
1.9.3p327 :003?> '42'
1.9.3p327 :004?> end
1.9.3p327 :005?> end
=> nil
1.9.3p327 :006 > A.new.foo
=> "42"
1.9.3p327 :011 > module Ext
1.9.3p327 :012?> def foo
1.9.3p327 :013?> 'ext'
1.9.3p327 :014?> end
1.9.3p327 :015?> end
=> nil
1.9.3p327 :016 > class AFancy
1.9.3p327 :017?> def call
1.9.3p327 :018?> a = A.new
1.9.3p327 :019?> a.extend Ext
1.9.3p327 :020?> a
1.9.3p327 :021?> end
1.9.3p327 :022?> end
=> nil
1.9.3p327 :023 > a1 = A.new
=> #<A:0x00000000e09b10>
1.9.3p327 :024 > a2 = AFancy.new.call
=> #<A:0x00000000e17210>
1.9.3p327 :025 > a3 = A.new
=> #<A:0x00000000e1bd38>
1.9.3p327 :026 > [a1, a2, a3].map(&:foo)
=> ["42", "ext", "42"]
Ok, I think I found out what was causing my issues...
In https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/mount.rb, in the definition of CarrierWave::Mount::Mounter, there are three references to record.class. This rightly references back to the main User class, which doesn't have the extended methods I loaded into the user's metaclass. So, I changed those to this: https://gist.github.com/4465172, and it seemed to work.
Also seems to continue to work if used normally like in the CarrierWave docs, so that's good too. Will continue testing it, though.
Firstly there's something odd about your contexts or at least the naming. Context do not return RolePlayers. Roles exist only inside a context. The role methods are not and should not be accessible outside the context. That said the standard way of dealing with DCI in ruby is method injection. This approach is not flawless but is so far the closest to pure DCI anyone have come in Ruby. There's an experimental library called alias_dci that might help.
EDIT
There now is a gem that makes injectionless DCI possible in Ruby. It's based on the work gone into Marvin, the first language to support injectionless DCI. The gem is called Moby and can be installed with
gem install Moby
it's currently still somewhat experimental but the smoke test of being able to implement the DCI examples from fullOO has been passed

What's the difference between defining class instance variable in a class and a method. Why?

Well, like the title shown, what's the difference between the two class instance variables below
class Document
#default_font = :Arial
...
end
And
class Document
def foo
#default_font = :Arial
end
...
end
Is there anyone can explain it to me. Thank you very much.
The second scenario isn't a class instance variable. It's a plain old instance variable.
In your first case, the variable is neither a class variable(which should have started with ##, nor an instance variable. It is simply a local variable not available outside the current scope, not even within the instance methods.
The second is an instance variable.
class Document
attr_accessor :var1, :var2
#var1 = 1
puts #var1 + 2
def initialize
#var2 = 4
puts #var2**2
#puts #var1 + 6
end
end
1.9.2p0 :208 > class Document
1.9.2p0 :209?> attr_accessor :var1, :var2
1.9.2p0 :210?> #var1 = 1
1.9.2p0 :211?> puts #var1 + 2
1.9.2p0 :212?>
1.9.2p0 :213 > def initialize
1.9.2p0 :214?> #var2 = 4
1.9.2p0 :215?> puts #var2**2
1.9.2p0 :216?> #puts #var1 + 6
1.9.2p0 :217 > end
1.9.2p0 :218?>
1.9.2p0 :219 > end
3
=> nil
1.9.2p0 :220 > d = Document.new
16
=> #<Document:0x1a2f0c #var2=4>
The #var1 + 6 inside the instance method gives an error.
I didn't see a final answer to the original post. I recently read the book "Eloquent Ruby" by Russ Olsen, so decided to add my take-away here.
The scope in which the first example defines the single-# variable is a class-scope, i.e., if the keyword self were to appear outside a method def but still within the class definition, it would reference the object Document, which is in turn an instance of the Class class.
The first example therefore defines a class instance variable, i.e., the variable #default_font is an instance variable of the object Document, a class object.
This class instance variable should only be accessed via the class level accessor methods, e.g., Document.default_font, rather than say, an_instance_of_Document.default_font
So if you define the attr_accessors:
class Document
#default_font = :Arial # this is a class instance variable,
# def self.default_font
# #default_font
# end
#
# def self.default_font=(font)
# #default_font = font
# end
# or replace the above two class methods with the following
class << self
attr_accessor :default_font
end
end
Here's some irb output:
1.9.3p429 :002 > require './document.rb'
=> true
1.9.3p429 :004 > Document.default_font
=> :Arial
1.9.3p429 :005 > Document.default_font = :Times
=> :Times
1.9.3p429 :006 > Document.default_font
=> :Times
1.9.3p429 :007 > doc = Document.new
=> #<Document:0x007fe6730d8228>
1.9.3p429 :008 > doc.default_font
NoMethodError: undefined method `default_font' for #<Document:0x007fe6730d8228>
The fact that the second example in the original post does not contain a single-# variable definition outside a method def, indicates that the #default_font here is an instance variable, which can only be referenced by an instantiated object of class Document.
class Document
def default_font=(font)
#default_font = font # this defines and set an instance variable
end
def default_font
#default_font # this defines an instance variable
end
# or above two defs can be replaced by the following
attr_accessor :default_font # this defines the instance variable and access methods
end
Here's some irb output:
1.9.3p429 :001 > require './document.rb'
=> true
1.9.3p429 :002 > Document.default_font
NoMethodError: undefined method `default_font' for Document:Class
1.9.3p429 :003 > doc = Document.new
=> #<Document:0x007ffbf1136f88>
1.9.3p429 :004 > doc.default_font
=> nil
1.9.3p429 :005 > doc.default_font = :Arial
=> :Arial
1.9.3p429 :006 > doc.default_font
=> :Arial

Class returning an array

I have a class like:
class Configuration
def self.files
##files ||= Array.new
end
end
Now instead of doing this:
irb(main):001:0> Configuration.files
=> [file, file, file]
I would like to be able to do this:
irb(main):001:0> Configuration
=> [file, file, file]
But I can't figure out how, any ideas?
#glenn jackman:
I was thinking of adding extra methods to a 'Configuration' constant that's a hash. So if I had...
Configuration = Hash.new
Configuration[:foo] = 'bar'
I wanted to be able to save this Configuration hash constant to be able to dump to and load from a YAML file I wanted to be able to use...
Configuration.load
Configuration.save
I wanted the Configuration class to look like
class Configuration
def self.save
open('config.yml', 'w') {|f| YAML.dump( self , f)}
end
def self.load
open('config.yml') {|f| YAML.load(f)}
end
end
You could possibly get the effect you are looking for by adding a method to the top level object which implicitly calls Configuration.files, but you can't really make a reference to a class invoke a method on it. You can alias the method to something shorter, but you will need to call something.
irb's top level just calls 'inspect' on the result, so by overriding it you can customize what you see in irb:
$ irb
>> class Configuration
>> def self.files
>> ##files ||= Array.new
>> end
>> def self.inspect
>> ##files.inspect
>> end
>> end
=> nil
>> Configuration.files << 1 << 2 << 3
=> [1, 2, 3]
>> Configuration
=> [1, 2, 3]
You could do something like this:
class Configuration
(class << self; self; end).module_eval do
def files
['foo','bar','baz']
end
def to_s
files
end
end
end
This would define the Configuration.files method and say that the to string conversion would return the result of that method. But I am really not sure why you would want to do this. It seems quite wrong.
wpc#wpc-laptop:~$ irb
irb(main):001:0> 'a'
=> a
irb(main):002:0> def A
irb(main):003:1> end
=> nil
irb(main):004:0> A
NameError: uninitialized constant A
from (irb):4
from :0
irb(main):005:0> class A
irb(main):006:1> end
=> nil
irb(main):007:0> A
=> A
irb(main):008:0> class A
irb(main):009:1> (class ['g']
irb(main):012:3> end
irb(main):013:2> def to_s
irb(main):014:3> g
irb(main):015:3> end
irb(main):016:2> end
irb(main):017:1> end
=> nil
irb(main):018:0> A
=> g
irb(main):019:0> class B
irb(main):020:1> class def to_s
irb(main):022:3> 'g'
irb(main):023:3> end
irb(main):024:2> end
irb(main):025:1> end
=> nil
irb(main):026:0> B
=> g
irb(main):027:0> class B
irb(main):028:1> class def files
irb(main):030:3> ['a','b','c']
irb(main):031:3> end
irb(main):032:2> def to_s
irb(main):033:3> files
irb(main):034:3> end
irb(main):035:2> end
irb(main):036:1> end
=> nil
irb(main):037:0> B
=> abc
The right solution is to using the class' method of to_s, but what it return is only the string; and then you can set the inspect for using. See the follows' detail for help.
class A
class<<self
def to_s
......
end
end
end

Resources