I saw a RubyGem with the following use:
f = Foo.new("joe")
f.say.hello #=> "Hello joe"
In my Gem, I'm trying to have the same syntax. I have many classes within the Module Dance, but once I create a new instance of Dance::Client, I can't access the other Classes. For example:
d = Dance::Client.new("key")
d::Genres.all # => errors out
The results I would like is:
d = Dance::Client.new("key")
d.genres.all
There's probably dozens of ways you could do this, so here's a few examples:
Client instance method returns Genre class
module Dance
class Client
def genres
Genre
end
end
class Genre
def self.all
# return all genres
end
end
end
Client instance method returns Genre collection class
module Dance
class Client
def genres
GenreCollection.new
end
end
class GenreCollection
def all
Genre.all
end
end
class Genre
def self.all
# return all genres
end
end
end
In the example you show, "say" is an instance method, returning something with a "hello" method.
Similarly, "genres" would be an instance property of a Dance module's Client instance.
If you're just trying to instantiate a client class you'd use Module:: Class notation, whereas your error example attempts to do that with an instance.
Related
In Rails we can call "Person.find_by_attribute" where 'attribute' is dynamically placed by attributes in the 'Person' migration. I want to replicate this logic n my Person class without rails and call 'find_by_attribute' for the attributes. What I have so far:
class Person
def attributes
[
{ id: 1, nice_attribute: "something" },
{ id: 2, nice_attribute: "another thing" },
]
end
def find_by_id(id)
attributes.select { |d| d[:id] == id }
end
But defining a find_by like this could bloat the class quickly. How can I achieve dynamic def setting?
I'm reasonably sure this is not how Rails does this, but here is some implementation that might assist you:
What you have defined there is a Singleton class and you should be aware of how to dynamically define attribute records in such a class before committing to that design. It might be a good choice, but you should be feeding that class an access point to search through records defined elsewhere. Regardless, the below will get you started:
class Person
##attributes = { id: Integer, nice_attribute: String }
# getter method
def self.attribute_types
##attributes
end
attribute_types.keys.each do |attr|
define_singleton_method :"find_by_#{attr}" do |value|
# logic
end
end
end
Then you will be able to call Person.find_by_id or Person.find_by_nice_attribute
When you implement it may be better for you to have a PersonRecord module that collects records from a database and the Person class might be an instance like so:
class Person
def initialise(opts ={})
define_find_by_methods
end
def attribute_keys
# stub method, but should be collecting this information dynamically
[ :id, :nice_attribute ]
end
def define_find_by_methods
attribute_keys.each do |attr|
# no longer using define_singleton_method here and define_method is a class method you will have to send it to self.class
self.class.send(:define_method, :"find_by_#{attr}") do |val|
# logic
end
end
end
end
So I've made quite a large application in Ruby, but I've realised it's quite unorganised to have everything as an instance method in one huge class, so I want to split it up into nested modules just so it's a bit more organised. I have searched on StackOverflow but it seems that it's actually not that common to use modules nested in a class.
I'm trying to understand how nested modules work by using a simpler example class:
class Phones
include Apps
include Call
attr_accessor :brand, :model, :price, :smartphone
def initialize(brand,model,price,smartphone=true)
#price = price
#brand = brand
#model = model
#smartphone = smartphone
#task = 'stand-by'
end
module Apps
public def camera
#task = __method__.to_s
puts "Took a picture!"
self
end
public def gallery
#task = __method__.to_s
puts "Photos!"
self
end
end
module Call
public def scall
#task = __method__.to_s
puts "Ring ring!"
self
end
end
end
Then I'm trying to run:
s7 = Phones.new('Samsung','S7 Edge',3000).Apps.camera
But I keep getting this error:
...phones.rb:3:in `<class:Phones>': uninitialized constant Phones::Apps (NameError)
The problem is your include calls are before the actual module definitions.
When you write a class definition, everything in there is executed immediately except stuff like method definitions. For example:
class Foo
puts 1 + 1
end
will print 2 immediately, it will not wait until you say Foo.new.
One way to fix it would be to move the include calls to the end of the class definition, or the modules to the top. You can also separate out the nested modules:
class Phones
module Apps
# your stuff here
end
end
class Phones
module Call
# stuff
end
end
# just make sure the previous modules have been defined
# by the time this code is run
class Phones
include Call
include Apps
end
I have a ToDo list program I'm writing for practice. My problem is that by separating concerns and making each list be a class while each task is also a class, I would like to be able to call the name of the list which the new task is being added to without having to pass the list name into the task class (either upon creation or later):
class HigherClass
def initialize
#higher_class_variable = unique_value
#instance_of_lower_class #keep variable empty for now
end
end
class LowerClass
def intitialize
#lower_class_variable = unique_value #this should be the same unique value as above
end
end
instance_of_higher_class = HigherClass.new
instance_of_higher_class.#instance_of_lower_class = LowerClass.new
instance_of_higher_class.#instance_of_lower_class.#lower_class_variable
#this should return the unique_value from the HigherClass instance
If you want to access attributes:
attr_reader :lower_class_variable
Then you can just do this:
#instance_of_lower_class.lower_class_variable
Same principle applies here for higher class:
class HigherClass
attr_writer :instance_of_lower_class
end
Then:
instance_of_higher_class.instance_of_lower_class = LowerClass.new
That all seems rather clunky considering you can do this:
class HigherClass
attr_accessor :unique
def initialize
#unique = unique_value
end
end
class LowerClass < HigherClass
def initialize
# Call superclass initialization
super
# Anything else you want here.
end
def something_using_unique
call_method(unique)
end
end
I'm using Ruby's metaprogramming methods creating a bunch of methods within a class. Within the class OmekaItem there are a bunch of methods of this form dc_title and dc_subject, and there are a bunch of methods of this form itm_field1 and itm_field2. I'd like to group those methods better. Ideally, given an instance of the class named item, I'd like call the methods this way:
item.dublin_core.title
item.item_type_metadata.field
and so on. Is there a way to do this?
This question has the code I'm working with.
Would something like the following work for you?
class OmekaItem
class DublinCore
def initialize(omeka_item)
#omeka_item = omeka_item
end
def title
#omeka_item.dc_title
end
def subject
#omeka_item.dc_subject
end
end
class ItemTypeMetadata
def initialize(omeka_item)
#omeka_item = omeka_item
end
def field1
#omeka_item.itm_field1
end
def field2
#omeka_item.itm_field2
end
end
def dublin_core
#dublin_core ||= DublinCore.new(self)
end
def item_type_metadata
#item_type_metadata ||= ItemTypeMetadata.new(self)
end
end
The methods on DublinCore and ItemTypeMetadata could be dynamically generated using define_method as appropriate.
I have the following method:
class Store < ActiveRecord::Base
def my_func(str)
puts str
end
end
I can't seem to call it from outside the class like this:
Store::my_func("hi")
Any idea why?
What you have defined is an instance method. Basically this means you can only call it on instances of that class.
store = Store.new
store.my_func("hi")
If you want a class method, you need to define it a little differently. Either:
class Store < ActiveRecord::Base
def self.my_func(str)
puts str
end
end
Or (more useful if you're defining a lot of class methods):
class Store < ActiveRecord::Base
class << self
def my_func(str)
puts str
end
end
end
The above two work because classes are also instances of the class Class, so the implicit receiver self in the above two examples is that instance (the class itself).
You call a class method like this:
Store.my_func("hi")