Question regarding two usage patterns
Hi-
So this isn't an "issue" so much as a discussion I'd like to have. Please feel free to tell me to have it elsewhere (or not at all), but I think it'd be a good idea to document some more advanced usage / design patterns in the wiki for people doing further extension / customization of their models.
For instance, I have a base-class that all my models inherit from, in addition to mixing in MotionModel::Model.
In this base class, I have a few things I'd like to get your feedback on, and see if you approve, in which case I could start an "advanced topics" or "design patterns" section to the wiki.
Since I am syncing MotionModel objects to the server, I add a create_from_server method which is called when I download objects from a server and save them. I do this so I can keep track of dirty, not-new records and sync them in a before_save, after_delete when they are actually new and not just downloaded from the server.
def self.create_from_server(args)
b = self.new(args)
b.add_to_store
b
end
Also I had the use-case come up where I had a model with certain immutable fields. They can be set on creation, or might sync from the server somehow, but can't ever be changed. To accomplish this I override a method called <field>=, for instance I had a model with a name field that couldn't change where I wrote the following:
def name=(val)
# Buddies names are immutable, that is they can only be set on creation.
if self.send(:name).nil?
_set_attr(:name, val)
else
raise "Cannot change the name of an existing buddy"
end
end
I could start to package this stuff into a full-blow REST support module ontop, as I'm slowly writing it in. But I don't know if you think that should be a separate gem or if you'd be open to a possibly pull req. Either way I'm sure it can be another adapter or some such. Thoughts?
I think the more info on the wiki the better. If you are using MotionModel for a backing store to a server database, it would be great if you could put information out there. If you're willing to write a gem, that would be incredible.
One of the problems with including something like remote pull into MotionModel is that it that's outside the domain and is better separated out into a different codebase. That way, people can choose the gem that best suits their needs (Firebase, some kind of Rails server, Parse, whatever).
Thanks!
Question: Why are before hooks not sufficient to make an attribute immutable?
Actually, rereading this, I think your use-cases are pretty compelling. The responsibility of what has changed on the client vis-a-vis the server is not addressed in MotionModel. I think a pretty simple mixin could be created to help you mark gray (needs sync resolution) objects. If you're up for contributing something like this I think it would be great.
I was on vacation for the last week and am just catching up.
I am currently using your existing new_record? method to see if the record needs to be added to the server. Otherwise I'm treating it like an update, and always updating the server when before_save is called.
I think / feel like when calling save on a model, it should always attempt to persist it to the server. The only "dirty" or special state is when the record is new, since then it needs to POST it to the server rather than update it, and set the model's primary key / database id.
With regard to IDs / primary_key values I hit another issue: you currently assume the "id" field is the primary / database key. However, it isn't always (for instance, I have a User model where the "name" field is the primary key). To solve this, I added a class method to my base class like such:
def primary_key(key=nil)
@primary_key = key if key
@primary_key ||= :id
end
... and then in my models I can do things like:
class User < BaseModel
primary_key :name
end
Thus in my before_save I can reference self.class.primary_key to get the name of the primary_key for the class.
I could add some more "grey record" logic to see which records need to be sync-ed, But would the existing dirty? method not be sufficient? I thought to check to see if dirty? is true, and only sync in this instance.