I have a ruby class that has an Array as one of its instance variables. I'm trying to figure out how to validate data that is being pushed onto the array.
class Something
def things
#things ||= Array.new
end
end
So I can declare an instance and add stuff to the array pretty easily this way.
#s = Something.new
#s.things << "one"
#s.things << "two"
I tried to create a class method named things<<(inString) to handle the validation but that is not valid syntax. So what approach can I take?
Try something like:
things << data if valid?(data)
where valid? is your validation method.
Example:
...
# will push only when quantity is greater than 0
def push(quantity)
things << item if valid?(quantity)
end
private
def valid?(number)
number > 0
end
If you want to have you own ThingsArray just create an Array subclass and override the push (<<) method to make the validation before pushing:
class ThingsArray < Array
def << (item)
return unless valid?(item)
super
end
private
def valid?(item)
item > 0
end
end
class Something
def things
#things ||= ThingsArray.new
end
end
How about this?
class MyThings < Array
def << (item)
# do your validation with item
self.push(item) if item.valid?
end
end
class Something
def things(item)
#things ||= MyThings.new
end
end
Related
I understand how to implement a (validating) setter (def item=), but how do I intercept the << operation on a field?
class Bla
attr_reader :item
def initialize
#item = []
end
# only called for =, +=, -= operations (not <<)
def item=(value)
puts "Changing value to #{value}"
# pretend that there is a conditional here
#item = value
end
# This is wrong:
#def item<<(value)
# puts "adding value #{value}"
# #item << value
#end
end
b = Bla.new
b.item = ['one'] # works
b.item += ['one'] # works
b.item << 'two' # bypasses my setter
I've tried def item<<(value), that doesn't seem to work.
When you call b.item << 'two', you are calling the << method on item directly. So you have a few options here:
Implement << directly on your Bla class, then use b << 'two':
# in class Bla
def <<(value)
# do validation here
#item << value
end
Use some other, nicer-named wrapper method name like add_item:
# in class Bla
def add_item(value)
# do validation here
#item << value
end
Use a special array class for #item which has a custom definition for <<:
class MyArray < Array
def <<(item)
# run validation here
super
end
end
# in Bla class
def initialize
#item = MyArray.new
end
I would probably go with option 2, it's the most simple and readable.
Here is an alternate suggestion:
class Bla
# DO NOT DEFINE A READER:
# attr_reader :item
def initialize
#items = []
end
def set_items(new_items)
puts "Changing value to #{new_items}"
# pretend that there is a conditional here
#items = new_items
end
def remove_item(item)
# pretend that there is a conditional here
#items -= items
end
def add_item(item)
# pretend that there is a conditional here
#items += items
end
end
By not exposing the #items object directly, you remain in full control of the interface for how the variable is manipulated.
(Unless a caller does something really hacky, like bla.instance_variable_get('#items')!!)
class Pit
def <<(dirt)
puts 'shovel ' + dirt
end
end
Pit.new << 'sandy loam'
# => shovel sandy loam
I'm not able to get the instance values of parent class to the child class, My code is like this.
class TimeLine
attr_accessor :tweets
def initialize(tweets=[])
#tweets = tweets
end
def print
puts tweets.join("\n")
end
end
class AuthenticateTimeLine < TimeLine
def print
authenticate!
super
end
def authenticate!
puts "authenticated!"
end
end
TimeLine.new([1,2,3,4,5])
authenticate_timeline = AuthenticateTimeLine.new
authenticate_timeline.print
When I call super on the child class, I'm getting empty array.
It's because you initialize it with empty array, you don't pass any argument to AuthenticateTimeLine.new, so default [] is taken (compare your TimeLine#initialize method). If you passed your array as argument, it would work:
authenticate_timeline = AuthenticatateTimeLine.new([1,2,3,4,5])
authenticate_timeline.print
# 'Works' now!
I have an array and I need to create a class method named "each" to yield or return (not sure what the difference is of those or which I need to use if any) each item in the array when the method is called.
Do I need to use return instead of yield or neither?
class Sum
def initialize
#sum = Array.new
end
def each
#sum.each do |item|
yield item
end
end
You could include Enumerable and implement each and get a lot of functionality for free.
class Sum
def initialize
#sum = []
end
def each &block
#sum.each &block
end
end
This will yield each item of the collection or if you do not provide a block it will return you an enumerator just like a normal Array would
Are you attempting to write a class that acts like an iterator? That provides it's own each form? If so, then this is the pattern for doing so with a ruby class:
class MyIterator
include Enumerable
def initialize data=[]
#data = data
end
def each
#data.each do |item|
yield item
end
end
end
m = MyIterator.new [1,2,3,4]
m.each do |item|
puts "item=#{item}"
end
puts m.map(&:next)
The for_each method iterates over the array (#data) and yields each of the values to the block.
As #jan-dvorak pointed out, including Enumerable and naming the method each gives additional benefits, such as being able to call map directly on the object.
Assuming that this is homework, and using the existing each method is not allowed:
class Sum
def initialize
#values = []
end
def homework_each
0.upto(#values.length-1){ |i| yield #values[i] }
end
end
However, your code as written works, modulo the fact that you're missing an end, and also you're missing any way to populate your array with values.
How would I create an attr_accessor to array?
for example
class MyClass
attr_accessor :my_attr_accessor
def initialize()
end
def add_new_value(new_array)
#my_attr_accessor += new_array
return #my_attr_accessor
end
end
my_class = MyClass.new
my_class.my_attr_accessor = 1
my_class.my_attr_accessor[1] = 2
my_class.my_attr_accessor.push = 3
my_class.add_new_value(5)
my_class.my_attr_accessor
=> [1, 2, 3, 5]
Just use an instance variable that points to an array and make an accessor from that instance variable.
Inside your class include something like this:
attr_accessor :my_attr_accessor
def initialize
#my_attr_accessor = []
end
Note that usingattr_accessor will allow you to change the value of the variable. If you want to ensure that the array stays, use attr_reader in place of attr_accessor. You will still be able to access and set array elements and perform operations on the array but you won't be able to replace it with a new value and using += for concatenation will not work.
If you are OK with the Array always existing, #david4dev's answer is good. If you only want the array to pop into existence on the first usage, and never want the user to be able to replace it with a new array (via assignment):
class MyClass
def my_attr_accessor
#my_attr_accessor ||= []
end
def add_new_value( value )
my_attr_accessor << value
end
def add_new_values( values_array )
my_attr_accessor.concat values_array
end
end
The user could still call my_class.my_attr_accessor.replace( [] ) to wipe it out.
Sorry for the poor title, I don't really know what to call this.
I have something like this in Ruby:
class Test
def initialize
#my_array = []
end
attr_accessor :my_array
end
test = Test.new
test.my_array << "Hello, World!"
For the #my_array instance variable, I want to override the << operator so that I can first process whatever is being inserted to it. I've tried #my_array.<<(value) as a method in the class, but it didn't work.
I think you're looking for this:
class Test
def initialize
#myarray = []
class << #myarray
def <<(val)
puts "adding #{val}" # or whatever it is you want to do first
super(val)
end
end
end
attr_accessor :myarray
end
There's a good article about this and related topics at Understanding Ruby Singleton Classes.
I'm not sure that's actually something you can do directly.
You can try creating a derived class from Array, implementing your functionality, like:
class MyCustomArray < Array
def initialize &process_append
#process_append = &process_append
end
def << value
raise MyCustomArrayError unless #process_append.call value
super.<< value
end
end
class Test
def initialize
#my_array = MyCustomArray.new
end
attr_accessor :my_array
end
Here you go...
$ cat ra1.rb
class Aa < Array
def << a
puts 'I HAVE THE CONTROL!!'
super a
end
end
class Test
def initialize
#my_array = Aa.new
end
attr_accessor :my_array
end
test = Test.new
test.my_array << "Hello, World!"
puts test.my_array.inspect
$ ruby ra1.rb
I HAVE THE CONTROL!!
["Hello, World!"]
$
a = []
a.instance_eval("alias old_add <<; def << value; puts value; old_add(value); end")
Very hackish, and off the top of my head ...
Just change 'puts value' with whatever preprocessing you want to do.
You can extend the metaclass of any individual object, without having to create a whole new class:
>> i = []
=> []
>> class << i
>> def <<(obj)
>> puts "Adding "+obj.to_s
>> super
>> end
>> end
=> nil
>> i << "foo"
Adding foo
=> ["foo"]
i extend the class, creating a method which provides access to the instance variable.
class KeywordBid
def override_ignore_price(ignore_price)
#ignorePrice = ignore_price
end
end