How can I limit a variable that belongs to new Class < Fixnum, between 0 and 255?
Or if I can't create a limit in subclass of Fixnim how to write my own class with limit?
Don't make the number a class, make access to that number limited as part of your class via a setter method.
Within your class never set the instance variable except via the setter method.
If you need to do this often, make a helper method for it:
class Module
def limited_value( name, range=0..100 )
attr_reader name
define_method(:"#{name}=") do |new_value|
if range.include?( new_value )
instance_variable_set :"##{name}", new_value
else
raise "Out of Bounds"
end
end
end
end
class Foo
limited_value :bar, 0..255
end
f = Foo.new
p f.bar #=> nil
f.bar = 10
p f.bar #=> 10
f.bar = 300
#=> tmp.rb:8:in `block in limited_value': Out of Bounds (RuntimeError)
You could alternatively choose to set the value to the nearest limit instead of raising a runtime error.
Write a non inherited class and use method_missing to call all functions from a instance variable, them, limit the return value.
class MyNum
instance_methods.each {|m| eval("undef " << m) }
def initialize(fixnum)
#num = fixnum
end
def method_missing(name, *args, &blk)
ret = #num.__send__(name, *args, &blk)
Numeric === ret ? MyNum.new([[ret, 0].max, 255].min) : ret
rescue NoMethodError
super
end
def inspect
"MyNum(#{#num.inspect})"
end
def class
MyNum
end
end
int = MyNum.new(50) # => MyNum(50)
int += 52 # => MyNum(102)
int.succ # => MyNum(103)
int + 300 # => MyNum(255)
int = -int # => MyNum(0)
int.zero? # => true
int == 0 # => true
Related
I'm new to Ruby, and expect to use methods inside of method or associate construct_const method with interpret method. Interpret method should simply return the value for this constant
def construct_const(value)
def interpret()
return #value
end
end
e = construct_const(0)
assert_equal(0, e.interpret)
In your posting, you are using value and #value. I guess this is a typo, since you are not refering value anywhere in your example code, so I assume that you wanted to use value in your interpret method, not #value.
You can define a method on the fly for a certain object using define_singleton_method, i.e.
def construct_const(value)
define_singleton_method(:interpret) { value }
end
With this, if you do then a
e.construct_const
the variable e will then have a new method 'interpret` attached to it.
When you define interpret method inside construct_const you get a :interpret symbol as the result of calling construct_const. Because every statement returns a value defining a method returns the method name.
> def interpret()
> return #value
> end
=> :interpret
So each time you call construct_const you define a method and return its name.
Also, you mismatch a local variable value and an instance variable #value.
You can use lambda or proc to return a function, the function will have call method:
def construct_const(value)
-> { value }
end
> e = construct_const(0)
=> #<Proc:0x000055c060a47f60#(irb):12 (lambda)>
> e.call
=> 0
You can define an anonymous class with a method interpret:
def construct_const(value)
my_class = Class.new do
def initialize(value)
#value = value
end
def interpret
#value # Here you can use an instance variable because it's inside of the anonymous class
end
end
my_class.new(value) # Pass a value to initialize a new instance
end
> e = construct_const(0)
=> #<#<Class:0x0000563663366c68>:0x0000563663366b50 #value=0>
> e.interpret
=> 0
A similar solution but with a predefined class:
class Interpreter
def initialize(value)
#value = value
end
def interpret
#value
end
end
def construct_const(value)
Interpreter.new(value)
end
> e = construct_const(0)
=> #<Interpreter:0x0000563663367b68 #value=0>
> e.interpret
=> 0
A solutions with Struct object:
def construct_const(value)
Struct.new(:interpret).new(value)
end
> e = construct_const(0)
=> #<struct interpret=0>
> e.interpret
=> 0
You've probably had better answers already, but as far as I can see, these seem to work (though I don't really know what I'm doing):
def construct_const(value)
mod = Module.new
mod.module_eval do
define_method :interpret do
value
end
end
obj = Object.new
obj.extend(mod)
obj
end
e = construct_const(0)
=> #<Object:0x00000000084a0e90>
e.interpret
=> 0
def construct_const(value)
obj = Object.new
obj.instance_eval do
#value = value
def interpret
#value
end
end
obj
end
e = construct_const(0)
=> #<Object:0x000000000a369098 #value=0>
e.interpret
=> 0
class Numeric
##currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if ##currencies.has_key?(singular_currency)
self * ##currencies[singular_currency]
else
super
end
end
end
puts 3.yen
# Output is
# 0.039
My question is why wouldn't this code work if we replaced ##currencies with an instant variable #currencies and added attr_reader :currencies
Something like this
class Numeric
#currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
attr_accessor :currencies
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if #currencies.has_key?(singular_currency)
self * #currencies[singular_currency]
else
super
end
end
end
puts 3.yen
# Output
# method_missing': undefined method `has_key?' for nil:NilClass (NoMethodError)
# from Untitled.rb:15:in `<main>'
Isn't 3 already an instant of a class Numeric, therefore, the setter currencies should be able to work and return the proper hash combo?
EDIT: so method_missing is a static method then? Why isn't it defined with self.method_missing?
Setting #currencies within the context of a class declaration sets an instance variable on the class itself:
Numeric.instance_variable_get(:#currencies)
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
On the other hand, #currencies within the method_missing class and currencies accessor refers to the #currencies variable on a particular instance of numeric, which isn't defined:
Numeric.new.instance_variable_get(:#currencies)
#=> nil
You could fix this by defining the accessor on the class itself, and calling that accessor in the method_missing method:
class Numeric
#currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
class << self
attr_accessor :currencies
end
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if self.class.currencies.has_key?(singular_currency)
self * self.class.currencies[singular_currency]
else
super
end
end
end
Numeric.currencies
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
There's still a problem with this approach though. Even though the currencies accessor now refers to an instance variable on the class (and not an instance variable on a particular instance of that class as was the case before), #currencies is still only set on the Numeric class, not any of its subclasses:
Fixnum.currencies
#=> nil
To fix this, you can either modify the attribute accessor to automatically provide a default value for each individual class (so Fixnum and Float would each have their own separate #currencies variable), or go back to using class variables, like this:
class Numeric
##currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
def self.currencies
##currencies
end
def self.currencies= new_currencies
##currencies = new_currencies
end
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if ##currencies.has_key?(singular_currency)
self * ##currencies[singular_currency]
else
super
end
end
end
Numeric.currencies
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
Fixnum.currencies
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
1.yen
#=> 0.013
Numeric.currencies['bitcoin'] = 394.03
#=> 394.03
5.bitcoin
#=> 1970.1499999999999
You could do this:
class Numeric
#currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
class << self
attr_reader :currencies
end
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if Numeric.currencies.key?(singular_currency)
self * Numeric.currencies[singular_currency]
else
super
end
end
end
The lines:
class << self
attr_reader :currencies
end
create a read accessor for the class instance variable #currencies.
You may be inclined to write (as I did initially):
if self.class.currencies.key?(singular_currency)
but that doesn't work, because method_missing is invoked on 3, an instance of Fixnum, a subclass of Numeric. Recall that subclasses don't have direct access to their ancestor's class methods. That's why we need to explicitly identify Numeric as the receiver.
As:
Fixnum.ancestors => [Fixnum, Integer, Numeric,...]
we see that:
if self.class.superclass.superclass.currencies.key?(singular_currency)
would work with Fixnum's, but not with other subclasses of Numeric: Bignum and Float.
Instance variable are only available on instances of a class. Class variables are available to the entire class.
In other words, the #currencies.has_key? in your code is undefined because it can't see the #currencies on line 2.
The problem is that you're initializing #currencies outside of a method. Any code that isn't inside a method is evaluated in the context of the class object, not an instance of the class.
Take a look at this example class:
class Foo
#bar = "baz"
def self.bar; #bar; end
def bar; #bar; end
end
Now let's look at the result of those two methods we defined.
Foo.bar # => "baz"
Foo.new.bar # => nil
This means that #bar belongs to the Foo class, not an instance of Foo.
Your problem can be solved by initializing #currency in a method, typically initialize:
class Numeric
def initialize
#currency = ...
end
end
For academic reasons, I'd like to make an instance of Ruby class act like a hash.
GOALS
Initialize MyClass instance with a hash # success
Request values from instance of myClass, like a hash # success
Then set properties as a hash # fail
Although some discussion exists, I tried what's out there (1, 2) with no success. Let me know what I'm doing wrong. Thanks!
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def set_prop(key, value)
myhash[key] = value
end
end
test = myClass.new({:a => 3}) #=> #<MyClass:0x007f96ca943898 #my_hash={:a=>3}>
test[:a] #=> 3
test[:b] = 4 #=> NameError: undefined local variable or method `myhash' for #<MyClass:0x007f96ca9d0ef0 #my_hash={:a=>3}>
You declared set_prop, but you're using []= in tests. Did you mean to get this?
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def []=(key, value)
my_hash[key] = value
end
end
test = MyClass.new({:a => 3}) # success
test[:a] # success
test[:b] = 4 # success
test.my_hash # => {:a=>3, :b=>4}
module HashizeModel
def [](key)
sym_key = to_sym_key(key)
self.instance_variable_get(sym_key)
end
def []=(key, value)
sym_key = to_sym_key(key)
self.instance_variable_set(sym_key, value)
end
private
def to_sym_key(key)
if key.is_a? Symbol
return ('#'+key.to_s).to_sym
else
return ('#'+key.to_s.delete('#')).to_sym
end
end
end
You should write it as test = MyClass.new({:a => 3}) and the below code should work.
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
#my_hash[key]
end
def []=(key,val)
#my_hash[key]=val
end
def set_prop(key, value)
#myhash[key] = value
end
end
test = MyClass.new({:a => 3})
test[:a]
test[:b]= 4
test.my_hash # => {:a=>3, :b=>4}
not quite understanding factory method here...
here is the respec line:
Temperature.from_celsius(50).in_celsius.should == 50
Here is what I have now:
getting errors...not quite sure how to satisfy this. thanks
class Temperature
attr_accessor :f
attr_accessor :c
def initialize(args)
#f = args[:f]
#c = args[:c]
end
def in_fahrenheit
#f or
(#c*9.0/5.0)+32
end
def in_celsius
#c or
(#f-32)*(5.0/9.0)
end
def self.from_celsius(c)
new c
end
end
This should help
class Temperature
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
#c *9.0 /5.0 +32
end
# factory pattern typically instantiates a new object
def self.from_celsius(c)
new c
end
end
puts Temperature.from_celsius(50).in_celsius #=> 50
puts Temperature.from_celsius(100).in_fahrenheit #=> 212
I would recommend against attr_accessor :c unless you want users to have public access to temp.c. Without it, users will be forced to use temp.in_celsius or temp.in_fahrenheit
You need to assign to :c in the initialize method. Then you need self.from_celsius to return a new instance of Temperature. You probably want something like this:
class Temperature
attr_accessor :c
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
9/5 * #c + 32
end
def self.from_celsius(num)
Temperature.new(num)
end
def self.from_fahrenheit(num)
Temperature.new((num-32)*5/9)
end
end
Now rspec shows true
1.9.1p378 :047 > Temperature.from_celsius(50).in_celsius.should == 50
=> true
1.9.1p378 :131 > Temperature.from_fahrenheit(32).in_celsius.should == 0
=> true
The reason you're getting "error: Can't covert symbol to integer –" is because you're in your Temperature.from_celsius(50) you're passing it an integer when you're supposed to pass it a key & symbol for the options hash.
initialized
class Temperature
def initialize(opts = {})
#options = opts
end
class factory method
def self.from_celsius(x)
Temperature.new(:c => x)
end
instance method
def in_celsius
if #options[:c] == nil
return (#options[:f]-32) * (5/9.to_f)
else
return #options[:c]
end
end
I tried to to extend the code from this question for keeping records of an attribute value. However, my code fails in the case of more than one attributes. Here is the code:
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s
attr_reader attr_name
ah=attr_name+"_history"
attr_reader ah
class_eval %Q{
def #{attr_name}= (attr_name)
#attr_name=attr_name
if #ah == nil
#ah=[nil]
end
#ah.push(attr_name)
end
def #{ah}
#ah
end
def #{attr_name}
#attr_name
end
}
end
end
Here a dummy class for testing
class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :bar1
end
f = Foo.new
f.bar = 1
f.bar = 2
f.bar1 = 5
p f.bar_history
p f.bar1_history
For some reason, f.bar and f.bar1 both return 5 and f.bar_history = f.bar1_history = [nil, 1, 2, 5]. Any idea why that is?
You were using #ah and #attr_name instead of ##{ah} and ##{attr_name} when getting/setting in the methods. This meant that they were always setting and returning the same instance variable, instead of different, dynamically named ones.
class Class
def attr_accessor_with_history(attr_name)
class_eval %{
attr_reader :#{attr_name}, :#{attr_name}_history
def #{attr_name}=(value)
##{attr_name} = value
##{attr_name}_history ||= [nil]
##{attr_name}_history << value
end
}
end
end
I've also generally cleaned up your code a little to make it (I think) clearer and more concise.