Hi guys wondering if anything like this was possible in ruby. Trying to use an instance variable for a conditional constant. I believe this isn't allowed is there another way of doing it?
class test
if #init_bool
VAR = 10
else
VAR = 15
end
def initialize(test_bool)
#init_bool = test_bool
end
end
While you certainly shouldn't do it (and even more likely you actually want to do something else instead) - you can do so, if you really, really insist on it.
class Foo
end
class Test < Foo
def self.new x
y= super
self.const_set :VAR, (y.instance_variable_get(:#init_bool) ? 10 : 15)
y
end
def initialize test_bool
#init_bool = test_bool
end
end
Then you would get this behaviour:
> Test.new false
=> #<Test:0x00007f9b519ea4f0 #init_bool=false>
> Test::VAR
=> 15
> Test.new true
=> #<Test:0x00007f9b51982e40 #init_bool=true>
> Test::VAR
=> 10
And you would also receive some warnings for every additional instance of Test you create (which is good - while the warnings won't stop you from what you are doing, they at least inform you about this unusual behaviour).
Related
I am trying to make a battleship game in Ruby, but when I try to create an instance of the game board, I get "wrong number of arguments, 0 for 1". I dont see where I am going wrong as the initialize definition clearly accepts arguments.
class Board
attr_reader :grid, :default_grid
def intitalize(grid = self.class.default_grid, random = false)
#grid = grid
make_random_board if random
end
def self.default_grid
grid = Array.new(10){Array.new(10)}
end
def count
grid.flatten.count{|x| x == :s}
end
def self.random
self.new(self.default_grid, true)
end
def empty?(position = nil)
return true if position.nil?
else
false
end
def full?
grid.flatten.none?(&:nil?)
end
def place_random_ship
if full?
raise "error, board is full"
end
space = [rand(grid.length),rand(grid.length)]
until empty?(space)
space = [rand(grid.length),rand(grid.length)]
end
self[space] = :s
end
def make_random_board(count = 10)
count.times do
place_random_ship
end
end
end
emptygrid = Array.new(2){Array.new(2)}
myGame = Board.new(emptygrid)
You have a typo in your code. You should be using initialize instead of intitalize
And i believe the error you might have been getting would be ArgumentError: wrong number of arguments (1 for 0)
Which is because of your typo, the default class initialize method was used, which doesn't take in any arguments.
And something unrelated that i noticed in your code. You have defined a method named count and use variables named count. This is a code smell and i would suggest naming the method differently, because down the line, this might cause some bugs, that you might find hard to debug.
When I try to run this code, nothing or nil shows up. I can't seem to understand why, since I thought classes that include modules can access its instance/class variables. I can print out the value just fine if I don't use garbtest and just use the garb= method to assign it a different value. It works fine without assigning it another value since I initialized it to 16 too. Is there something about the instance/class variables in the module Test that makes it equal to nil? Furthermore, when I try to assign garb to #myg + ##vit it says there is no such method for the nil class. I think this further confirms my suspicion that those variables are somehow nil. Thank you.
module Test
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test;
def initialize(n)
#garb = n
end
attr_accessor :garb;
def garbTest
#garb = #myg;
end
def exo
return 50;
end
end
ryu = Xy.new(16);
ryu.garbTest;
puts "#{ryu.garb}";
Because #myg is not shared variable. It is private property of module Test, thus while you included Test, #myg didn't come into Xy due to the mixin, it wouldn't come also by default. But, "Why nil?" - Because, instance variable, class variables are like that. Before initialising/defining them, if you attempt to use them, it will simply give you nil.
Small program to prove myself and Ruby :-
module Test
#x = 10
##y = 11
end
class Foo
include Test
end
Foo.instance_variable_defined?(:#x) # => false
Test.instance_variable_defined?(:#x) # => true
Foo.class_variable_defined?(:##y) # => true
Test.class_variable_defined?(:##y) # => true
You can define reader method inside Test singleton class, and then you can use it. Look below
module Test
class << self
attr_reader :myg
end
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test
def initialize(n)
#garb = n
end
attr_accessor :garb
def garbTest
#garb = Test.myg
end
def exo
return 50
end
end
ryu = Xy.new(16)
ryu.garbTest # => 9
I have this code:
l = lambda { a }
def some_function
a = 1
end
I just want to access a by the lambda and a special scope which has defined a already somewhere like inside some_function in the example, or just soon later in the same scope as:
l = lambda { a }
a = 1
l.call
Then I found when calling l, it is still using its own binding but not the new one where it was called.
And then I tried to use it as:
l.instance_eval do
a = 1
call
end
But this also failed, it is strange that I can't explain why.
I know the one of the solution is using eval, in which I could special a binding and executing some code in text, but I really do not want to use as so.
And, I know it is able to use a global variable or instance variable. However, actually my code is in a deeper embedded environment, so I don't want to break the completed parts if not quite necessary.
I have referred the Proc class in the documentation, and I found a function names binding that referred to the Proc's context. While the function only provided a way to access its binding but cannot change it, except using Binding#eval. It evaluate text also, which is exactly what I don't like to do.
Now the question is, do I have a better (or more elegant) way to implement this? Or using eval is already the regular manner?
Edit to reply to #Andrew:
Okay, this is a problem which I met when I'm writing a lexical parser, in which I defined a array with fixed-number of items, there including at least a Proc and a regular expression. My purpose is to matching the regular expressions and execute the Procs under my special scope, where the Proce will involved some local variables that should be defined later. And then I met the problem above.
Actually I suppose it is not same completely to that question, as mine is how to pass in binding to a Proc rather than how to pass it out.
#Niklas:
Got your answer, I think that is what exactly I want. It has solved my problem perfectly.
You can try the following hack:
class Proc
def call_with_vars(vars, *args)
Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
end
end
To be used like this:
irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3)
=> 3
irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1)
=> 4
This is not a very general solution, though. It would be better if we could give it Binding instance instead of a Hash and do the following:
l = lambda { |a| foo + a }
foo = 3
l.call_with_binding(binding, 1) # => 4
Using the following, more complex hack, this exact behaviour can be achieved:
class LookupStack
def initialize(bindings = [])
#bindings = bindings
end
def method_missing(m, *args)
#bindings.reverse_each do |bind|
begin
method = eval("method(%s)" % m.inspect, bind)
rescue NameError
else
return method.call(*args)
end
begin
value = eval(m.to_s, bind)
return value
rescue NameError
end
end
raise NoMethodError
end
def push_binding(bind)
#bindings.push bind
end
def push_instance(obj)
#bindings.push obj.instance_eval { binding }
end
def push_hash(vars)
push_instance Struct.new(*vars.keys).new(*vars.values)
end
def run_proc(p, *args)
instance_exec(*args, &p)
end
end
class Proc
def call_with_binding(bind, *args)
LookupStack.new([bind]).run_proc(self, *args)
end
end
Basically we define ourselves a manual name lookup stack and instance_exec our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding, it can also be used to build up much more complex lookup chains:
l = lambda { |a| local + func(2) + some_method(1) + var + a }
local = 1
def func(x) x end
class Foo < Struct.new(:add)
def some_method(x) x + add end
end
stack = LookupStack.new
stack.push_binding(binding)
stack.push_instance(Foo.new(2))
stack.push_hash(:var => 4)
p stack.run_proc(l, 5)
This prints 15, as expected :)
UPDATE: Code is now also available at Github. I use this for one my projects too now.
class Proc
def call_with_obj(obj, *args)
m = nil
p = self
Object.class_eval do
define_method :a_temp_method_name, &p
m = instance_method :a_temp_method_name; remove_method :a_temp_method_name
end
m.bind(obj).call(*args)
end
end
And then use it as:
class Foo
def bar
"bar"
end
end
p = Proc.new { bar }
bar = "baz"
p.call_with_obj(self) # => baz
p.call_with_obj(Foo.new) # => bar
Perhaps you don't actually need to define a later, but instead only need to set it later.
Or (as below), perhaps you don't actually need a to be a local variable (which itself references an array). Instead, perhaps you can usefully employ a class variable, such as ##a. This works for me, by printing "1":
class SomeClass
def l
#l ||= lambda { puts ##a }
end
def some_function
##a = 1
l.call
end
end
SomeClass.new.some_function
a similar way:
class Context
attr_reader :_previous, :_arguments
def initialize(_previous, _arguments)
#_previous = _previous
#_arguments = _arguments
end
end
def _code_def(_previous, _arguments = [], &_block)
define_method("_code_#{_previous}") do |_method_previous, _method_arguments = []|
Context.new(_method_previous, _method_arguments).instance_eval(&_block)
end
end
_code_def('something') do
puts _previous
puts _arguments
end
I'm trying to DRY up some code, and I feel like Ruby's variable assignment must provide a way to simplify this. I have a class with a number of different instance variables defined. Some of these are intended to be hidden (or read-only), but many are public, with read/write access.
For all of the variables with public write-access, I want to perform a certain method after each assignment. I know that, in general, I can do this:
def foo=(new_foo)
#foo = new_foo
post_process(#foo)
end
def bar=(new_bar)
#bar = new_bar
post_process(#foo)
end
However, it seems that there should be a nice way to DRY this up, since I'm doing essentially the same thing after each assignment (ie, running the same method, and passing the newly-assigned variable as a parameter to that method). Since I have a number of such variables, it would be great to have a general-purpose solution.
Simpler solution
If you assign those variables in batch, you can do something like this:
kv_pairs = {:foo => new_foo_value,
:bar => new_bar_value}
kv_pairs.each do |k, v|
self.send(k.to_s + '=', v)
post_process(v)
end
Metaprogramming
Here's some ruby magic :-)
module PostProcessAssignments
def hooked_accessor( *symbols )
symbols.each { | symbol |
class_eval( "def #{symbol}() ##{symbol}; end" )
class_eval( "def #{symbol}=(val) ##{symbol} = val; post_process('#{symbol}', val); end" )
}
end
end
class MyClass
extend PostProcessAssignments
hooked_accessor :foo
def post_process prop, val
puts "#{prop} was set to #{val}"
end
end
mc = MyClass.new
mc.foo = 4
puts mc.foo
Outputs:
foo was set to 4
4
I want to do some checking in a writer accessor. My first idea was returning a boolean.
class MyClass
def var=(var)
#var = var
# some checking
return true
end
end
m = MyClass.new
retval = (m.var = 'foo')
=> "foo"
Can I set a return value in a writer accessor? If yes, how can I get this value?
I would use set_var(var) instead of what you are trying to do, an attribute writer is assumed to just work. What you are trying to do is nonstandard and non-obvious to the next poor person to use your code. (It may just be yourself) I would throw an exception if bad input is sent or something rather exceptional happens.
You want this behavior
Correct
>>temp = object.var = 7
=> 7
Wrong
>>temp = object.var = 7
=> false
The = operator should always return the value that was passed to it. Ruby uses implicit returns which is uncommon in programming languages. Double check the returns when you use method=().
class Test
def var=(var)
#var = var
return true
end
end
t1, t2 = Test.new, Test.new
t1.var = 123 # evaluates to 123
# Why is it impossible to return something else:
t1.var = t2.var = 456
As stated in the comment: I believe it's impossible to change the return value in order to allow chained assignments. Changing the return value would probably be unexpected by the majority of Ruby programmers anyway.
Disclaimer: I tested the code above but I've found no explicit references to verify my statement.
Update
class Test
def method_missing(method, *args)
if method == :var=
# check something
#var = args[0]
return true
else
super(method, *args)
end
end
def var
#var
end
end
t = Test.new
t.var = 123 # evaluates to 123
t.does_not_exists # NoMethodError
It really seems to impossible! The above sample suggests that the return value isn't related to the var= method at all. You cannot change this behavior - it's the fundamental way how Ruby assignments work.
Update 2: Solution found
You could raise an exception!
class Test
def var=(var)
raise ArgumentError if var < 100 # or some other condition
#var = var
end
def var
#var
end
end
t = Test.new
t.var = 123 # 123
t.var = 1 # ArgumentError raised
t.var # 123
Also, per your example, to clean things up you could do the following:
class MyClass
attr_accessor :var
end
m = MyClass.new
m.var = "Test"
puts m.var # => "Test"
In regards to your question though, are you asking if you can return a value other than what you set for the value of the object?
Update
Check out this project: Validatable. It adds ActiveRecord like validations to your class attributes.
Quick scenario:
class Person
include Validatable
validates_presence_of :name
attr_accessor :name
end
class PersonPresenter
include Validatable
include_validations_for :person
attr_accessor :person
def initialize(person)
#person = person
end
end
presenter = PersonPresenter.new(Person.new)
presenter.valid? #=> false
presenter.errors.on(:name) #=> "can't be blank"
I know this is a late response to the party...
It's hard to know what you are attempting to do with the class. You mentioned wanting to check it before saving... to a database? file?
What would you like to happen in the case of a bad attribute value?
By using a boolean return, you effectively "hide" the error (because you will forget about the weird thing of having to check the return value of a setter).
While you can do what others suggested
Use Validator plugin
Raise an error
You can also consider an isValid? method like many classes that end up getting persisted do. Then the save can check the state of the object and throw an error.
Bottom line:
Don't do what you posted
Do throw an error (by any of the posted solutions)
One way to help drive the solution is to consider writing the behavior you are looking for first (i.e., BDD). Then, work your way further into what the class should do by TDD -ing using RSpec to get the desired class "contract" that needs to emerge to support the desired behavior.