Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>FWIW, this looks fine to me. I might look at <a href="http://github.com/stffn/declarative_authorization">declarative_authorization</a> to manage your roles, which might end up somewhat involved, particularly from the UI standpoint. Managing users with multiple roles seems more appropriate than STI in this situation, because, as you say, a user can be both manager and tenant, etc. </p> <p>One approach to keeping the code for manager, tenant and owner separate, while allowing for the use of all three roles in a single instance, might be to dynamically include modules representing those roles based on what roles any given user has. You'd still have a base class of User, but instead of using STI, which would limit you to a single inheritance structure, you could mix in modules based on the roles each user has, which might simply pivot on what Properties belong_to a given user during initialization. </p> <p>For this I might create a User factory that can inspect the user for the various roles it plays and extend the <a href="http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html">singleton class</a> of that user with the corresponding module: extend with the Tenant module for users that have tenant properties, extend with the Manager module for users that have managed properties, etc. </p> <p>From the standpoint of declarative_authorization, you could declare the role_symbols similarly based on whether associations like managed_properties were populated, for instance:</p> <pre><code>def role_symbols @roles ||= {:manager =&gt; :managed_properties, :tenant =&gt; :rented_property, :owner =&gt; :owned_properties}.map do |k,v| k if !send(v).blank? end.compact end </code></pre> <p>Or something similar. You'd probably want to set the roles AND include the corresponding module at the same time. Ruby and Rails gives you many options via <a href="http://ruby-metaprogramming.rubylearning.com/">metaprogramming techniques</a> to dynamically decorate with the functionality individual models need. My approach may or may not be appropriate for your application, but there are countless other ways you could approach the problem and keep your code clean and DRY.</p> <p>Overall it seems to me your data model is sound, and your instinct not to use STI to manage multiple roles is correct. It doesn't look to me like you've overthought it -- I think you're on the right track. It's actually pretty Rails-y for a first pass. </p> <p>EDIT: You know, the more I think about it, I'm not sure what the benefit of keeping the manager/tenant/owner functionality in separate modules really is. In my former incarnation as a Java/C# guy I would have been all about the <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">SRP</a>/<a href="http://en.wikipedia.org/wiki/Inversion_of_control">IOC</a> and total separation of concerns. But in Ruby and Rails it's not nearly as big of a deal, since it's dynamically typed and coupling is not nearly as big, or at least the same sort, of concern that it is in statically-typed environments. You might be perfectly fine just putting all of the individual role functionality in the single User model and not worry with the modules, at least not yet. </p> <p>I'm on the fence here, and would welcome input from others. To me one of the benefits to Ruby classes, as opposed to Java classes/packages or .NET classes/assemblies, is that you can always refactor as needed and not be nearly as concerned about which class, package, namespace, dll or jar is coupled to another, etc. I'm not saying SRP isn't important in Ruby, not at all. But I'm not nearly as paranoid about it as I used to be. </p> <p>EDIT: Paul Russell makes an excellent point. I think you should seriously consider allowing multiple tenants/managers/landlords per property. In Rails this could be expressed through a relational table and a has_many :through association, plus STI to describe the different types of relationships. I also think it will be necessary to invert the relationship between User (as Tenant) and Property. A Property can have more than one Tenant, but a Tenant can't live at more than one property. (or maybe they can? Doesn't seem right, but...) </p> <p>Maybe something like (this is very quick and dirty, so forgive any missed details please):</p> <pre><code>class PropertyRole &lt; ActiveRecord::Base belongs_to :user belongs_to :property end class Managership &lt; PropertyRole # Manager functionality here end class Ownership &lt; PropertyRole # Owner functionality here end class User &lt; ActiveRecord::Base belongs_to :residence, :class_name =&gt; 'Property', :foreign_key =&gt; 'residence_id' has_many :managerships has_many :ownerships has_many :owned_properties, :through =&gt; :ownerships, :classname =&gt; 'Property' has_many :managed_properties, :through =&gt; :managerships, :classname =&gt; 'Property' end class Property &lt; ActiveRecord::Base has_many :tenants, :class_name =&gt; 'User', :foreign_key =&gt; 'residence_id' has_many :managerships has_many :ownerships has_many :owners, :class_name =&gt; 'User', :through =&gt; :ownerships has_many :managers, :class_name =&gt; 'User', :through =&gt; :managerships end </code></pre> <p>Like I said, this is quick and dirty, a high-level first pass. Note that we've now created Managership and Ownership roles that can contain Manager and Owner-specific functionality, eliminating the former dilemma as to whether to silo that functionality in separate modules. </p> <p>** Note also that I've inverted the Tenant/Property role as well -- I think this was a necessary change to your domain. Obviously a residence can have more than one tenant. It seems to me (at the moment) that you can keep the tenant-specific functionality on the User model. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload