Defining a method to update ruby object property - ruby

Hey I am having trouble with the following question to be solved using ruby.
Question
Write a function that provides change directory (cd) function for an abstract file system.
Notes:
Root path is '/'.
Path separator is '/'.
Parent directory is addressable as '..'.
Directory names consist only of English alphabet letters (A-Z and a-z).
For example:
path = Path.new('/a/b/c/d')
puts path.cd('../x').current_path
should display '/a/b/c/x'.
Note: Do not use built-in path-related functions.
My Answer
class Path
def initialize(path)
#current_path = path
end
def current_path
#current_path
end
def cd(new_path)
if new_path.include? ".."
z = new_path.split("/")
b = #current_path
a = b.split('/')
a.shift
a.pop
#current_path = a.push(z[z.length-1]).join("/")
else
end
end
end
path = Path.new('/a/b/c/d')
path = path.cd('../x')
However this returns a string instead of an object from the 'path' variable.

You need to create a chain method. There are 2 ways to address it.
The immutable one - just create new instance of the class instead of modifying, e.g. return Path.new(calculated_path)
The mutable one - modify #current_path and return self in the end of the method #cd

After you've changed #current_path in the object, just return the object ('self')
#current_path = a.push(z[z.length-1]).join("/")
return self

Related

Recursive in Ruby - Return method in itself

I want to return the method in itself
def self.open_folder(file)
Dir.glob(file+"*") do |subfiles|
if File.directory?(subfiles)
open_folder(subfiles) ###Problem here
end
if File.file?(subfiles)
open_file(subfiles)
end
end
end
What I want is to return the "open_folder" to keep open the sub-folder. I got an error
block in open_folder': stack level too deep
Can you help me to find the solution for it?
If you just want to apply some method to every file in subdirectories, you could use :
Dir.glob("**/*").select{ |path| File.file?(path) }.each{ |file| open_file(file) }
This code works for me:
def open_file(file)
# Do your stuff here
puts file
end
def open_folder(file)
Dir.glob("#{file}/*") do |subfile|
File.directory?(subfile) ? open_folder(subfile) : open_file(subfile)
end
end
open_folder('path/to/directory')
NOTES:
You don't need to define the methods as self.* if you are running this code directly in irb or outside any class defined by you.
I used string interpolation (#{foo}) instead of concatenating the string.
Appending a '/*' to file path will look for all files and directories directly under the parent (not the nested subdirectories and files).
Instead of using 2 ifs, you can use elsif in this case as only 1 of the condition can be true in each iteration.

Ruby - Invoking a class from a CONSTANT that contains the class name

I have a class that calls different suppliers to find if an item is available. How do I execute the class that each constant returns?
class ItemProvider
ADAPTER_ONE = Providers::ItemFromSupplierOne
ADAPTER_TWO = Providers::ItemFromSupplierTwo
def get_item(item)
id = ItemDetail.new(item)
%w(ADAPTER_ONE ADAPTER_TWO).each do |provider|
item_detail = provider.new(id)
break if item_detail.valid?
end
item_detail
end
Your problem is that you aren't making an array that contains the constants' values; you're making an array with the strings "ADAPTER_ONE" and "ADAPTER_TWO". The %w() syntax always makes an array of strings — it doesn't resolve variable names.
What you want is to change your get_item code to something like this:
def get_item(item)
id = ItemDetail.new(item)
[ADAPTER_ONE, ADAPTER_TWO].each do |provider|
item_detail = provider.new(id)
break item_detail if item_detail.valid?
end or nil # break automatically makes the block return the value you break with
end
As an aside, personally, I think I'd rewrite it like this:
def get_item(item)
id = ItemDetail.new(item)
[ADAPTER_ONE, ADAPTER_TWO].map {|provider| provider.new(id) }.find &:valid?
end
Yup you have an array of strings not constants but if you want to go down that road in using classes from strings well it will be nice if you look at http://blog.sidu.in/2008/02/loading-classes-from-strings-in-ruby.html#.UuGdmGQ1i2w .Maybe it is not directly related to your problem but it is a good read.

Can I use methods for String class within the String class in Ruby?

My new method for a string object in ruby is supposed to return a hash of the count of each character within a string (loaded in from a .txt file) and I am probably trying to go about it the easy way, however I can't seem to make it work without passing the object. I was wondering if there was a way to do this without passing a string. Any help would be appreciated.
Here is my code
class String
def frequency
Object.downcase
Object.gsub("\n", " ")
h = {}
h["A:"] = Object.count('a')
h["B:"] = Object.count('b')
h["C:"] = Object.count('c')
h["D:"] = Object.count('d')
h["E:"] = Object.count('e')
h["F:"] = Object.count('f')
h["G:"] = Object.count('g')
h["H:"] = Object.count('h')
h["I:"] = Object.count('i')
h["J:"] = Object.count('j')
h["K:"] = Object.count('k')
h["L:"] = Object.count('l')
h["M:"] = Object.count('m')
h["N:"] = Object.count('n')
h["O:"] = Object.count('o')
h["P:"] = Object.count('p')
h["Q:"] = Object.count('q')
h["R:"] = Object.count('r')
h["S:"] = Object.count('s')
h["T:"] = Object.count('t')
h["U:"] = Object.count('u')
h["V:"] = Object.count('v')
h["W:"] = Object.count('w')
h["K:"] = Object.count('x')
h["Y:"] = Object.count('y')
h["Z"] = Object.count('z')
return h
end
end
Sounds like you are talking about self, which is the ruby keyword that refers to the current object. Note that self is implied if you just call the method. So to use your example
class String
def frequency
count('a')
end
end
would return the number of as in the string
"asdfa".frequency #=> 2
Just a note, but your current method is very repetitive, and you might want to think about taking advantage of a loop to reduce the amount of code. Also you are not counting capital letters :)
Rather than a very long, un-DRY method that iterates your object 26 times, how about using some Ruby:
def frequency
Hash[downcase.gsub(/[^a-z]/,'').chars.group_by(&:to_s).map{|char, group| ["#{char.upcase}:", group.size]}]
end
You can break this apart onto separate lines if you find it easier to read (and to look up the methods in the API [1]):
def frequency
intermediate_variable = downcase
intermediate_variable = intermediate_variable.gsub(/[^a-z]/,'') # only keep a-z characters
intermediate_variable = intermediate_variable.chars.group_by(&:to_s) # split the string into its component characters and then group that array by the characters (run this on an array to see what it does :-) could also have written it `.group_by{|e|e}`
intermediate_variable = intermediate_variable.map{|char, group| ["#{char.upcase}:", group.size]} # map the array of grouped characters into an array of character and counts (formatting the 'character' how you would like your hash key configured
Hash[intermediate_variable] # make a hash of the characters and their counts
end
[1] http://ruby-doc.org/core-2.0.0/Enumerable.html http://ruby-doc.org/core-2.0.0/String.html
Here is the version I used which is a complete copy of the Rosetta Letter Frequency:
#!/usr/bin/env ruby
def letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
end
In ruby you can just open the class and add the method, like:
class String
def my_method
my_method_code
end
end
Then you just call the method string.my_method. However in your case I would rather use a Ruby module. Here is a code sample, very similar to a class but cleaner imho:
#!/usr/bin/env ruby
module MyString
def self.letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
return freq
end
end
p MyString.letter_frequency('absd')
Modules are more suited for adding your own classes into projects avoiding name colliding and creating mixins.
I would just create a hash like this:
class String
def frequency
chars.each_with_object(Hash.new(0)) do |char, h|
h["#{char.upcase}:"] += 1 if char[/[[:alpha:]]/]
end
end
end

Setting variable A with name stored in variable B

I have the following two variables:
a = 1;
b = 'a';
I want to be able to do
SOMETYPEOFEVALUATION(b) = 2;
so that the value of variable a is now set to 2.
a # => 2
Is this possible?
Specifically, I am working with the Facebook API. Each object has a variety of different connections (friends, likes, movies, etc). I have a parser class that stores the state of the last call to the Facebook API for all of these connections. These states are all named corresponding to the the GET you have to call in order to update them.
For example, to update the Music connection, you use https://graph.facebook.com/me/music?access_token=... I store the result in a variable called updated_music. For books, its updated_books. If I created a list of all these connection type names, I ideally want to do something like this.
def update_all
connection_list.each do |connection_name|
updated_SomeTypeOfEvalAndConcatenation(connection_name) = CallToAPI("https://graph.facebook.com/me/#{connection_name}?access_token=...")
end
end
Very new to both Rails and StackOverflow so please let me know if there is a better way to follow any conventions.
Tried the below.
class FacebookParser
attr_accessor :last_albums_json,
def update_parser_vars(service)
handler = FacebookAPIHandler.new
connections_type_list = ['albums']
connections_type_list.each do |connection_name|
eval "self.last_#{connection_name}_json = handler.access_api_by_content_type(service, #{connection_name})['data']"
end
#self.last_albums_json = handler.access_api_by_content_type(service, 'albums')['data']
end
end
And I get this error
undefined local variable or method `albums' for #<FacebookParser:0xaa7d12c>
Works fine when I use line that is commented out.
Changing an unrelated variable like that is a bit of a code smell; Most programmers don't like it when a variable magically changes value, at least not without being inside an enclosing class.
In that simple example, it's much more common to say:
a=something(b)
Or if a is a more complex thing, make it a class:
class Foo
attr_accessor :a
def initialize(value)
#a = value
end
def transform(value)
#a = "new value: #{value}"
end
end
baz = "something"
bar = Foo.new(2)
bar.a
=> 2
bar.transform(baz)
bar.a
=> "new value: something"
So while the second example changes an internal variable but not through the accessor, at least it is part of an encapsulated object with a limited API.
Update Ah, I think the question is how do do like php's variable variables. As mu suggests, if you want to do this, you are probably doing the wrong thing... it's a concept that should never have been thought of. Use classes or hashes or something.
how about
eval "#{b}=2"
and with instance variables you can also do instance_variable_set("#name", value)
EDIT:
you can also use send method if you have a setter defined(and you have), try this:
class FacebookParser
attr_accessor :last_albums_json,
def update_parser_vars(service)
handler = FacebookAPIHandler.new
connections_type_list = ['albums']
connections_type_list.each do |connection_name|
send("last_#{connection_name}_json=",
handler.access_api_by_content_type(
service, connection_name)['data']))
end
end
end
problem with your original code is that
eval ".... handler.access_api_by_content_type(service, #{connection_name})"
would execute
... handler.access_api_by_content_type(service, albums)
# instead of
... handler.access_api_by_content_type(service, 'albums')
so you had to write
eval ".... handler.access_api_by_content_type(service, '#{connection_name}')" <- the quotes!
this is why people usually avoid using eval - it's easy to do this kind of mistakes
These sort of things are not usually done using local variables and their names in Ruby. A usual approach could include hashes and symbols:
data = Hash.new
data[:a] = 1 # a = 1
b = :a # b = 'a'
and then, later
data[b] = 2 # SOMETYPEOFEVALUATION(b) = 2
data[:a] # => 2

Is it acceptable having parameter in class constructor?

a rubygem I'm writing and that is useful for counting word occurrences in a text, I choose to put 3 parameters in class constructor.
The code is working, but I want to refactor it for niceness.
In your experience, it's easier to read/mantain/use as API a class with a constructor with no params and a lot of setters/getters method or a code like this one, with all the params in the constructor?
TIA
Paolo
def initialize(filename, words, hide_list)
if ! filename.nil?
#filename = filename
#occurrences = read
else
#filename = STDIN
#occurrences = feed
end
#hide_list = hide_list
#sorted = Array(occurrences).sort { |one, two| -(one[1] <=> two[1]) }
#words = words
end
You could do it the rails way, where options are given in a hash:
def initialize(filename = nil, options = {})
#hide_list = options[:hide_list]
#words = options[:words]
if filename
#filename = filename
#occurrences = read
else
#filename = STDIN
#occurrences = feed
end
#sorted = Array(occurrences).sort { |one, two| -(one[1] <=> two[1]) }
end
Then you can call it like this:
WC.new "file.txt", :hide_list => %w(a the to), :words => %w(some words here)
or this:
wc = WC.new
wc.hide_list = %w(a the is)
wc.words = %w(some words here)
In my experience I can tell that the very reason for allowing constructor parameters in most languages, apart from the fact of increasing the easiness in class instantiation, is to make it easy to use the API.
Favoring constructor, over getter/setter instantiation, also helps immutability, that is, creating an object thorough its constructor, and not letting anyone modify its properties later on.
I dont know how it is in Ruby, but in other languages you usually put those arguments in the constructor signature that are needed to initialize the object into a valid state. All other state can be set through setters.

Resources