Should I use DataMapper entities only for persistence purposes? - ruby

I'm creating a non-Rails application and using DataMapper as ORM.
For entities which will be mapped to SQL tables I declare classes which include DataMapper::Resource.
The question is. Is it okay to use the instances of these classes as plain objects (pass to methods, manipulate values etc.)? Or they should be used only for persisting data (for instance in Repository classes)?
I'm new in the Ruby world and do not know the conventions.
If I have a User entity, which has methods creates, all etc., is it a good idea to create another class User, which only will store information (will have state - fields and no methods)? Analogue of POJO (Plain old java object) in Java?

I can see creating a wrapper class for a plain object list having some benefits. As you mention in the comment, if you want to store data in different ways then writing distinct classes is useful.
For typical DataMapper or ActiveRecord usage, though, I don't think it's common to create wrapper classes for plain-object lists, especially if you're not adding any methods to the collection. The main reason why it's not common is that query results in ActiveRecord or DataMapper are array-like already. Additionally, you're not really gaining any added functionality by converting your model instances to hashes. Let me show some example:
# collections are array-like
User.all.map(&:name) == User.all.to_a.map(&:name)
# converting a record to a hash doesn't add much
user = User.first
user_hash = user.attributes
user.name == user_hash[:name]
That being said, there is one caveat, and that has to do with chainable methods in the ORM:
# this is valid chaining
User.all.where(name: "max")
# this raises a NoMethodError for 'where'
User.all.to_a.where(name: "max")
where is a ORM method, not an array method. So if you convert the query result to an array you couldn't access it. For this reason, making a distinction between arrays and query collections is useful.
But how much benefit do you really get from creating an empty wrapper class?
class RecordsInMemory
def initialize(query_collection)
#list = query_collection.map(&:attributes)
end
end
records_in_memory = RecordsInMemory.new(User.all)
records_in_memory.list.map(&:name)
# versus ...
records_in_memory = User.all.map(&:attributes)
records_in_memory.map(&:name)
if you think in the long run you will add methods to the plain-object list, then you should make it into a class. But otherwise I think using clearly-named variables suffices.

Related

In Ruby, what are the use cases for adding methods to an instance's singleton class?

Thanks to some other posts and reading, I understand singleton/meta classes. And I understand why we'd want to use them on a class. But I still don't understand why we'd want to use them on instance objects. And I've yet to see it in practice.
I'm referring to something like this:
class Vehicle
def odometer_reading
# some code
end
end
my_car = Vehicle.new
def my_car.open_door
# some code
end
At first thought, this seems like a bad idea as it would lead to difficulties in understanding the code and debugging.
Why would we want to do this? What are some examples of when this is a good idea?
One example is using it for testing purposes: creating mock and double objects, stubbing methods. Debugging is somewhere nearby: re-defining the logging method for a specific object that you suspect is mis-behaving, so that the log info is printed directly to console (or more info is printed) during the debug session.
Another example is dealing with special cases - instead of inheritance you can do just that. Starting from a classical example if you use two types of Employees, say, Engineers and SalesPersons, for which the rules of compensation calculation are different, you can put the common logic into the Employee class, then inherit the other two classes from it and implement their own calculate_salary methods there. Now, if there is an outlier - a star salesman that you have agreed to a different compensation scheme with, a CEO with a very special scheme, etc - instead of creating a whole sub-class for this special employee, you can just define this method for a specific object representing that employee.
The third example is dealing with an object lifecycle and performance considerations. Instead of having a long case of various states in some processing method. E.g. for a file-reading class that transparently caches the entire file in the background (I know a too-simplistic-for-real-life approach, but just as a model) all read requests while the file is not entirely read should check if the requested data is already in the cache or should be read from disk. Once the file is fully read they always go from the cache. Instead of having the if (case if there are more states) to deal with this you could simply re-define the read method at the object-level once the file is fully read to the cache. For this simple example it doesn't lead to any sizable performance benefit (if any benefit at all), but for more complex cases that may be worth it.
You wouldn't add them using def, that's a rather rigid way of doing it, but instead by using something like define_method or extend.
Although this is not the sort of thing you'd do on a routine basis, it does mean you can do some rather unusual things. ActiveRecord in Rails produces results in the form of an Array with additional methods added on to perform other operations.
An Object-Relationship Mapper would be a case where you'd probably want to do this. Sometimes, depending on how you fetch a record, the methods available differ significantly. Being able to add those dynamically means each fetched object can be completely customized even if they have the same class and general-purpose methods.
Another example: You have an array of hashes and you want each hash to have a method-call getter and setter. Something like:
user = HashOnSteroids.new(name: 'John')
user[:name] # => 'John'
user[:name] = 'Joe'
user.name # => 'Joe'
user.name = 'John'
user.set(name: 'Jim', age: 5)
This means you cannot write standard method definitions in the class as each hash will have a different set of keys (method names). This means you have to resort to defining singleton methods so each object has its own set of methods (not a pack of shared methods).
Warning: Using singleton methods for this use case is highly inefficient. A sneaky method_missing is faster and uses way less memory as it doesn't have to allocate a billion of proc objects.

How to provide parameters when initializing a ActiveRecord model in Rails

I have read allot of questions on here about different ways to initialize a ActiveRecord model properly when dealing with initializing values and is always in relation to providing default values. A great answer I came across helped clarify different ways.
However, if it is frowned upon to override the ActiveRecord base initialize method, what is the proper way to provide parameters to an ActiveRecord model when you want to initialize with values as you would in a standard Ruby class initialize(arg1, arg2...) method.
so you can
obj = MyObject.new(Obj1, some_num)
The only thing I have come across was actually overrideing the initialize method but calling super first in the initialize. However, this was frowned on because ActiveRecord's base class uses allocate in allot of cases to instantiate an AR object and therefore could end up sidestepping the entire initialize method.
So, maybe there is another fundamental reason why I am not finding providing initial values as a standard practice in RoR?
I know I can use validates in the object to validate an object can't be saved without meeting requirements, such as having all the proper attributes set. But I was approaching this particular object, so that it wouldn't be created without the required attributes when it is initialized to begin (since at initialization we would have all that information and if retrieving from the DB we would have all those values).
Can someone help direct me?
extra question
If there is an accepted way to do the above, there seems to be another area that needs to be considered when initializing objects with values and that is when the object is initialized from being retrieved from the DB. Not handling the initializing values properly can possibly override what is retrieved from the DB (or I have read when related to setting attributes to default values). So if that needs to be handled properly, how do we do that?
You can provide a hash of values when you initialize an ActiveRecord object. This is used in the create action of the controller to create a new object based on the data collected in the new action.
For example:
def create
#book = Book.new(params[:book])
where params is a hash of the values for the new object.

How to extend an existing Ruby model to support persistence

I have a gem which implements my entire business logic, so that I can use it in different applications. Now, one of these applications requires persistence. How do I easily extend my existing Ruby models to support persistence? Should I monkey patch them?
To give you a bit of a background, my model objects are usually just built from XML or JSON files, but now I need to store them in an relational database.
Are there common patterns for this problem? Should I write new model objects that support persistence and map between my legacy objects and the new model objects or should I extend the existing ones to be representable in a database?
Any tips, hints, and links are highly welcome.
I am not sure that I fully understand your question. However, the DataMapper library can be very easily used to add persistence to an already existing object model after the fact, for two reasons:
It doesn't rely on class inheritance (like e.g. ActiveRecord does) but on mixin inheritance, and you can inherit from as many mixins you like, which means you won't have to change the inheritance tree of your object model just to add DataMapper to it.
The object-relational-mapping is declared explicitly in the model, not inferred from the data-store. This means that you can have very complex mappings between the data-store and your models, unlike the rather simple 1:1 table == class, row == object, column == attribute mapping of ActiveRecord.
Now, whether or not you will manage to keep the persistence aspect fully orthogonal, and e.g. in a separate gem, that's another question. You could indeed keep it in a separate library that just opens up all the model classes and include DataMapper::Resource and declare all the properties. This will allow you to still deploy your object model gem without persistence, but the persistence gem will obviously be rather tightly coupled to the object model gem.

How can I specify HYDRATE_ARRAY in the table convenience methods?

With Doctrine 2, is there any way to specify HYDRATE_ARRAY when I use the convenience methods EntityRepository::find or EntityRepository::findOneById? Do I have to write my own DQL query, and use that instead?
I'm not sure, but I think you can't.
You will have to write your own DQL, or fetch an object and later serialize it to an array.
Second approach is probably slower, but it comes handy for testing environment. I have used beberleis serialization class found here.
It works ok, except you can't serialize one-to-many and many-to-many relations in an object, in other words anything that should serialize as an collection of objects.
Btw. if you don't have php5.4, just replace the trait with an abstract class, and have all your model object inherit from that class

OO Design: Multiple persistance design for a ruby class

I am designing a class for log entries of my mail server. I have parsed the log entries and created the class hierarchy. Now I need to save the in memory representation to the disk. I need to save it to multiple destinations like mysql and disk files. I am at a loss to find out the proper way to design the persistence mechanism. The challenges are:
How to pass persistence
initialization information like
filename, db connection parameters
passed to them. The options I can
think of are all ugly for eg:
1.1 Constructor: it becomes ugly as I
add more persistence.
1.2 Method: Object.mysql_params(" "),
again butt ugly
"Correct" method name to call each
persistance mechanism: eg:
Object.save_mysql, Object.save_file,
or Object.save (mysql) and
Object.save(file)
I am sure there is some pattern to solve this particular problem. I am using ruby as my language, with out any rails, ie pure ruby code. Any clue is much welcome.
raj
Personally I'd break things out a bit - the object representing a log entry really shouldn't be worrying about how it should save it, so I'd probably create a MySQLObjectStore, and FileObjectStore, which you can configure separately, and gets passed the object to save. You could give your Object class a class variable which contains the store type, to be called on save.
class Object
cattr_accessor :store
def save
##store.save(self)
end
end
class MySQLObjectStore
def initialize(connection_string)
# Connect to DB etc...
end
def save(obj)
# Write to database
end
end
store = MySQLObjectStore.new("user:password#localhost/database")
Object.store = store
obj = Object.new(foo)
obj.save
Unless I completely misunstood your question, I would recommend using the Strategy pattern. Instead of having this one class try to write to all of those different sources, delegate that responsibility to another class. Have a bunch of LogWriter classes, each one with the responsibility of persiting the object to a particular data store. So you might have a MySqlLogWriter, FileLogWriter, etc.
Each one of these objects can be instantiated on their own and then the persitence object can be passed to it:
lw = FileLogWriter.new "log_file.txt"
lw.Write(log)
You really should separate your concerns here. The message and the way the message is saved are two separate things. In fact, in many cases, it would also be more efficient not to open a new mysql connection or new file pointer for every message.
I would create a Saver class, extended by FileSaver and MysqlSaver, each of which have a save method, which is passed your message. The saver is responsible for pulling out the parts of the message that apply and saving them to the medium it's responsible for.

Resources