I have a class called Orders. I want to keep an array of all the "Orders" instances so I can .each map/reduce/or otherwise query them.
Later I would like to Marshal "dump" and "load" the "Orders" array of objects.
My plan is to add each new object created onto an ##all_orders array stack on initialization. Then I can do my .each method on the ##all_orders array, looping through all "orders" objects.
Am I approaching this the right OOP way? (code snippet to get the idea)...
class Orders
##all_orders = Array.new
attr_accessor :order_no, :customer
def initialize(order_no, customer)
#id, #customer = order_no, customer
#order_lines = Array.new
##all_orders << self
end
The proper Object-Oriented way to do this is to have a container you're putting the orders into. This can be a plain-old array or a special container class.
The big problem here is you don't have a proper context for storing the "all orders" data. All orders relevant to what? The entire application? If that's the case you need the concept of an order book or a database to store them.
For example:
class OrderBook < Array
end
order_book = OrderBook.new
order_book << Order.new(...)
Note that I've renamed Orders to Order as that's more accurate, singular name.
Auto-magically adding instances to a container is almost always a recipe for disaster. That sort of thing should be handled using a design pattern like model-controller. It's a huge assumption that you'd want to include it in the global orders pool.
I would consider using dependency injection to pass in a list object to your order instances. Best practice in OOP is to decouple code and try to give each object only one responsibility (the Single Responsibility Principle). This helps keep your code maintainable and easily changeable. The way you're approaching it now, the Order class is doing both order-related stuff AND storage of the list. In my example below, a list object can be passed to the Order object when the order is instantiated and all the order need know is that the list object should respond to the #add method. This way, if the implementation for the list has to change, like making the list object use a Redis store, for example, then you don't need to change both the Order class and the OrderList class. You would only update the OrderList class to deal with Redis in the #add method. Additionally, you no longer have an internal dependency in the Order class for handling list responsibilities, so the order is only doing what it should be.
class OrderList
attr_reader :items
def initialize
#items = []
end
def add(order)
#items << order
end
end
class Order
def initialize(opts)
order_list = opts[:order_list]
order_list.add self
end
end
order_list = OrderList.new
order1 = Order.new order_list: order_list
order2 = Order.new order_list: order_list
puts order_list.list.inspect
Related
I am modelling a report as a class and each column value as an accessor. Each value in the report needs to be accessed from the database. But this makes the class look quite fat and RubyMine warns me of too many methods in class.
class Report
attr_accessor :name, :col1, :col2, :col3 .... :col15
def col1
db.find({x: 1})['some_var']
end
def col2
db.find({y: 4})['some_other_var']
end
and so forth for each attribute...
end
Since each getter is essentially single line that makes call to database, is there a simpler way to declare these vars without being in a method?
I don't want to set these in the initialize method as these reports will be subclassed and child reports will not have all/some of these attributes.
You can use meta-programming to create attr_accessor like methods on the fly.
For Example:
class Report
def initialize(attributes)
attributes.each do |attribute|
define_singleton_method :"#{attribute}" do |hash_param, string_param|
db.find(hash_param)[string_param]
end
end
end
end
Then you can create new report object and pass attribute names as follow:
r = Report.new(["n","m"])
Now you can call n and m methods on the r object
r.m({val1: "val1"}, "val2")
r.n({val2: "val1"}, "val2")
#Rahul based on answer to my question, the only advice I can give here then is to use best OOP design principles. Subclass and modularize where possible as well as using ruby meta-programming. If you need the methods, they have to be written somewhere. But if you only need getters, consider attr_reader instead, unless you'll need setters too.
If you can get the column names you can use dynamic method definition with something like this assuming db is magically defined somewhere you have not made clear, we'll assume it's a connection to the database.
class Report
db.column_names.each do |col|
define_method(col.to_sym) { db.find(options={}) }
end
end
If you just want RubyMine to stop nagging you, I assume it's using rubocop and you can see this post for how to override rules.
https://www.jetbrains.com/help/ruby/2017.1/rubocop.html
or
https://github.com/rubocop-hq/rubocop
I'm new to ruby. I'm calling a REST api from ruby client, and that API returns a model. Model contains nested complex objects, upto 4 level on average. Lets say, the Model is Customer and I have created a class Customer in my ruby client app. Imagine the following:
outer class:
class Customer
def CustomerCards=(address) #inner complex object
#address = address
end
#... other complex objects
end
inner class CustomerCard
class CustomerCard
def CardDetails=(carddetails) #inner-inner complex object
#carddetails= carddetails
end
#... other complex objects
end
inner->inner class CardDetails
class CardDetails
def Cardvalidations=(cardvalidations) #inner-inner-inner complex object
#cardvalidations = cardvalidations
end
#... other complex objects
end
inner->inner->inner class CardValidations
class Cardvalidations
def MaxTranxLimit=(maxTranxLimit ) #inner-inner-inner complex object
#maxTranxLimit= maxTranxLimit
end
#... other complex objects
end
In this case, I have to manually JSON.parse each property (which returns hash), and cast it to corresponding object, upto n-level. Which doesn't seem an efficient solution. (Imagine, I have around 8 complex properties in Customer model, and further each property's model contains another 8 complex properties)
Question: Is there any generic way to do this?
I understand, we cannot define type of property in ruby class, but as I'm new to Ruby, and I must be missing something. and there must be some solution to this, as REST calls are very common.
AFAIK there is no generic way to do this, perhaps partly because API responses never match your model structure 1/1. But it would be pretty easy to implement this. I would go with something like MyModel.load_from_hash factory method and then recursively call corresponding factory methods from children models. You could then extract this functionality to a Concern and there it is.
Suppose I have a user class as below
class User
def initialize
test
end
def test
name = ['name1','name2', 'name3']
returned_value = test1(name)
lots of if else condition based on returned_value
end
def test1(name)
test2(name)
end
def test2(name)
test3(name)
end
def test3(name)
end
end
And this user class has some methods like test, test1.. and each method do some calculation one single array and then return the array. Now based on the returned value test method add or delete elements from the array. This become very ugly with lot of if else condition because returned value could be blank, one element, two dimensional array and so one.
But if I use an instance variable then I don't have to return any value and so I don't need any if else statement based on the returned value and my code looks clean. For example
class User
def initialize
#name = ['name1','name2', 'name3']
test
end
def test
test1
end
def test1
test2
end
def test2
test3
end
def test3
end
end
My question is, should I use instance variable or not in this case? An explanation would be nice.
Update:
In short it would be, I want to manipulate one single array in different methods of a Class. Now, I can create the array inside test method and then pass it to other methods (first case in my above example) or I can create the array in initialize method and then I don't have to pass the array every time(second case in my example above). So my questions is
Is my first/second approach is bad/okay? Or both approach is okay to use?
If you have data that needs to be accessible to many methods in a class, then instance variables is a good way to manage that.
I generally use instance variables in PORO classes for two reasons
To store information, usually at initialization, that many of the
class methods need access to. This is what you're asking about
above.
To memoize resource intensive methods.
For example to avoid doing an api call every time I access customer_data I would replace
def customer_data
HTTParty.get... (some api call)
end
with
def customer_data
#customer_data ||= HTTParty.get... (some api call)
end
#customer_data is only used within that method, but if I call the method multiple times only one api call is needed. This assumes (of course) that for a given class object there is only one associated customer.
I have a class that represents a collection. I included the Enumerable module into it and defined the method #each, so that I get all its methods.
But the problem is that Enumerable's methods don't keep the same class. So, for example, if my class is named Collection, and if I do Collection#select, I would like that the result's class is also Collection (instead of Array). Is there a way how to achieve this?
Since Enumerable#select is designed to return an array, you need to tell somewhere how to map that to a Collection instance. That means, you explicitly need to define Collection#select. Otherwise Ruby will not know the mapping rule from the original array result of Enumerable#select to a Collection instance.
Unfortunately, Ruby's Collection Operations are not type-preserving. Every collection operation always returns an Array.
For collections like Sets or Trees, this is merely annoying, because you need to always convert them back into the type you want to have. But for example for an infinite lazy stream of all prime numbers, this is catastrophic: your program will either hang or run out of memory trying to construct an infinitely large Array.
Most Collection APIs either eliminate duplicate code or are type-preserving, but not both. E.g. .NET's Collection API mostly eliminates duplicate code, but it always returns the same type: IEnumerable (equivalent to Ruby's Enumerator). Smalltalk's Collection API is type-preserving, but it achieves this by duplicating all Collection Operations in every Collection type.
The only Collection API which is type-preserving yet eliminates duplication is Scala's. It achieves this by introducing the new concept of Collection Builders, which know how to efficiently construct a Collection of a specific type. The Collection Operations are implemented in terms of Collection Builders, and only the Collection Builders need to be duplicated … but those are specific to every Collection anyway.
If you want type-preserving Collection Operations in Ruby, you need to either duplicate all Collection Operations in your own Collection (which would be limited to your own code), or redesign the entire Collection API to use Builders (which would require a major redesign of not only your own code but also the existing Collections including every third-party Collection ever written).
It's clear that the second approach is at least impractical if not impossible. The first approach also has its problems, though: Collection Operations are expected to return Arrays, violating that expectation may break other people's code!
You can take an approach similar to Ruby 2.0's lazy collection operations: you could add a new method preserve_type to your API which returns a proxy object with type-preserving Collection Operations. That way, the departure from the standard API is clearly marked in the code:
c.select … # always returns an Array
c.preserve_type.select … # returns whatever the type of c is
Something like:
class Hash
def preserve_type
TypePreservingHash.new(self)
end
end
class TypePreservingHash
def initialize(original)
#original = original
end
def map(*args, &block)
Hash[#original.map(*args, &block)
# You may want to do something more efficient
end
end
Another way could be to make Collection a proxy for the underlying array:
class Collection
def initialize( items= nil )
#items = items || []
end
def respond_to_missing?(method_name, include_private = false)
Enumerable.instance_methods.include? method_name
end
def method_missing name, *args, &block
if #items.respond_to? name
res = #items.send name, *args, &block
res.kind_of?( Array ) ? Collection.new(res) : res
else
super
end
end
end
in IRB:
col = Collection.new [1,2,3]
=> #<Collection:0x0000010102d5d0 #items=[1, 2, 3]>
col.respond_to? :map
=> true
col.map{|x| x * 2 }
=> #<Collection:0x000001009bff18 #items=[2, 4, 6]>
The following worked for me. I found only the filtering methods needed to be redefined. If we redefine all methods that return Array, this includes collect which should not be redefined.
include Enumerable
def select(&block)
self.class.new(super.select(&block))
end
def reject(&block)
self.class.new(super.reject(&block))
end
I'm writing a framework for querying the Mediawiki API. I have a Page class which represents articles on the wiki, and I've also got a Category class, which is-a Page with more specific methods (like being able to count the number of members in the category. I've also got a method Page#category? which determines if an instantiated Page object is actually representative of a Mediawiki category page, by querying the API to determine the namespace of the article.
class Page
def initialize(title)
# do initialization stuff
end
def category?
# query the API to get the namespace of the page and then...
namespace == CATEGORY_NAMESPACE
end
end
class Category < Page
# ...
end
What I would like to do is be able to detect if the user of my framework tries to instantiate a Mediawiki category using a Page object (ie. Page.new("Category:My Category")), and if so, instantiate a Category object, instead of a Page object, directly from the Page constructor.
It seems to me that this should be possible because it's reminiscent of single table inheritance in Rails, but I'm not sure how to go about getting it to work.
Ok, couple of things:
You can't convert an instance of a class A to an instance of A's subclass B. At least, not automatically. B can (and usually does) contain attributes not present in A, it can have completely different constructor etc. So, AFAIK, no OO language will allow you to "convert" classes that way.
Even in static-typed languages, when you instantiate B, and then assign it to a variable a of type A, it is still instance of B, it is not converted to its ancestor class whatsoever.
Ruby is a dynamic language with powerful reflection capabilities, so you can always decide which class to instantiate in the runtime - check this out:
puts "Which class to instantiate: "
class_name = gets.chomp
klass = Module.const_get class_name
instance = klass.new
So, no need for any conversion here - just instantiate the class you need in the first place.
Another thing: as I mentioned in the comment, method category? is simply wrong, as it violates OOP principles. In Ruby, you can - and should - use method is_a?, so your check will look like:
if instance.is_a? Category
puts 'Yes, yes, it is a category!'
else
puts "Nope, it's something else."
end
This is just a tip of the iceberg, there's lot more about instantiating different classes, and another question I have linked in the comment can be a great starting point, although some code examples there might confuse you. But it is definitely worth understanding them.
Edit: After re-reading your updated question, it seems to me that the right way for you would be to create a factory class and let it do the detecting and instantiating different page types. So, user wouldn't call Page.new directly, but rather call something like
MediaWikiClient.get_page "Category:My Category"
and get_page method would instantiate corresponding class.
Why not something like this? Being able to do that is a good enough reason to do it!
class Page
def self.new(title)
if self == Page and is_category?(title)
Category.new(title)
else
super
end
end
def self.is_category?(title)
# ... (query the API etc.)
end
def initialize(title)
# do initialization stuff
end
def category?
# query the API to get the namespace of the page and then...
namespace == CATEGORY_NAMESPACE
end
end
class Category < Page
# ...
end
You could define a method that instantiate the class and returns the instance.
This is know as Factory Pattern
class PageFactory
def create(title) # the pattern uses "create".. but "new" is more Ruby' style
namespace = title[/\A[^:]+(?=:)/]
# matches the prefix, up to and excluding the first colon.
if namespace == CATEGORY_NAMESPACE
Category.new(title)
else
Page.new(title)
end
end
end
class ClientClass
def do_something()
factory = PageFactory.new
my_page = factory.create('Category:Foo')
my_page.do_something()
end
end