Best way to handle resource associations - ruby-on-rails

In my Rails app, I currently have a parent resource 'developments' and a child resource 'lots'.
But I need to have different types of developments now, i.e. apartments, houses, townhouses that still associate with their own lots. These different types of developments need to have different fields on their forms.
What is the simplest and easiest way to handle this?
I've currently thought of these approaches:
add a new column dev_type_id integer to developments that is associated with a development type (i.e. 1 = apartments etc.) and then have that as a condition to render different fields of the form. The trouble with this is I don't know how to practically build it and pass through the id from the index view to the form path
have multiple forms for development with different fields on them. I'm not sure how to set the routes for this approach and if it would work in practice
Multiple parent resources to single child: split the development into separate scaffolds for apartment, houses etc. and have lots belong to all of them through has_and_belongs_to_many. This just seems like it might be really messy.
Multiple parent resources to multiple children: Complete restructure so that Apartments has child Apartment_lots, Townhouses has child Townhouse_lots etc. etc. This seems like it would work as intended, but there has to be a simpler way, right? But I can see the benefit of being able to really customise each for the future if I need to.
Any suggestions spoken from experience, the rails way or anything is really appreciated.

Related

Dry up Rails Active Record query conditions

In my Ruby on Rails project, I have a mailer that basically prepares a daily digest of things that happened in the system for a given user. In the mailer controller, I am gathering all the relevant records from the various models according to some common pattern (within a certain date, not authored by this user, not flagged, etc) and with minor differences from model to model.
There are half a dozen of models involved here (and counting), and most of them have unified column names for certain things (like date of publishing, or whether an item is flagged by admin or not). Hence, the 'where's that go into query are mostly the same. There are minor differences in conditions, but at least 2 or 3 conditions are exactly the same. I easily assume there may be even more similar conditions between models, since we are just starting the feature and haven't figured out the eventual shape of the data yet.
I basically chain the 'where' calls upon each model. It irritates me to have 6 lines of code so close to each other, spanning so far to the right of my code editor, and yet so similar. I am dreaded by the idea that at some point we will have to change one of the 'core' conditions, munging with that many lines of code all at once.
What I'd love to do is to move a core set of conditions that goes into each query into some sort of Proc or whatever, then simply call it upon each model like a scope, and after that continue the 'where' chain with model-specific conditions. Much like a scope on each model.
What I am struggling with is how exactly to do that, while keeping the code inside mailer. I certainly know that I can declare a complex scope inside a concern, then mix it into my models and start each of queries with that scope. However, this way the logic will go away from the mailer into an uncharted territory of model concerns, and also it will complicate each model with a scope that is currently only needed for one little mailer in a huge system. Also, for some queries, a set of details from User model is required for a query, and I don't want each of my models to handle User.
I like the way scopes are defined in the Active Record models via lambdas (like scope :pending, -> { where(approved: [nil, false]) }), and was looking for a way to use similar syntax outside model class and inside my mailer method (possibly with a tap or something like that), but I haven't found any good examples of such an approach.
So, is it possible to achieve? Can I collect the core 'where' calls inside some variable in my mailer method and apply them to many models, while still being able to continue the where chain after that?
The beauty of Arel, the technology behind ActiveRecord query-building, is it's all completely composable, using ordinary ruby.
Do I understand your question right that this is what you want to do?
def add_on_something(arel_scope)
arel_scope.where("magic = true").where("something = 1")
end
add_on_something(User).where("more").order("whatever").limit(10)
add_on_something( Project.where("whatever") ).order("something")
Just ordinary ruby method will do it, you don't need a special AR feature. Because AR scopes are already composable.
You could do something like:
#report_a = default_scope(ModelA)
#report_b = default_scope(ModelB)
private
def default_scope(model)
model.
where(approved: [nil, false]).
order(:created_at)
# ...
end

Rails Capitalization/Pluralization Rules

When dealing with resources (e.g. users) different parts of the rails app refer to them in one of several ways, some capitalized/singular, some lowercase/plural etc. At times this seems logical (e.g. a method for several resources vs. just one) but at other times it seems arbitrary...
Is there any easy way of remembering how to access them from different parts of the app?
Most of the time, you will need to access different models across the app. And you would always access them with singular name with first letter uppercase'd like User, Tweet. Regarding controllers, I don't think so you would ever to access a controller from some other controller.
Remember, if are using raw SQL, and you want to access the table of a model, that would always be in plural and all lower case, like users for User, and tweets for Tweet.
Regarding routes, they are always accessed through lowercase words, and deciding whether singular or plural -- it depends upon the context.
If you are accessing all tweets, the route method will be tweets_path, and if want one tweet, then tweet_path(1) or edit_tweet_path(1) where 1 being the id of the tweet that you want to show or edit.
And for classes: everywhere in Rails, and generally speaking in Ruby, they would always be singular, and uppercase'd.

"Virtual associations" with Rails

I'm looking for some guidance on how to implement what I am referring to as "virtual associations" into my model in rails.
I'll preface this with the disclaimer that I have considered going down the real associations path (HABTM) but as far as I can see this would conflict with concepts that I already have implemented.
I currently have a Project model which can be associated with User objects via roles.
As an example, a Project may have many site_managers, construction_managers and project_managers.
A site_manager can also be a project_manager for the same or different projects (rules out single table inheritance).
With the Rolify gem this is fairly straightforward to implement. I can assign any of the roles above to a particular user in a s pecific project with sample_user.add_role(:site_manager, sample_project).
One of my goals is to be able to create a form where I can setup a new project, and assign users to roles from using a multi-select list. So as an example, my form would have the following input to assign selected users as site manager for the new project:
= f.input :site_managers, collection: User.all, input_html: { multiple: true } (Formtastic DSL)
This is where things get slightly complicated. I have managed to implement a custom getter/setter for the site_maanagers attribute where I can take a hash of user_ids passed by the form and fetch/update the appropriate records as needed.
However this implementation is far from being similar to that of a real association, where I could do things like adding a single user to the site_managers with sample_project.site_managers << sample_user.
At the moment I am also unable to set the array of site_managers using user instances. My custom setter only takes user_ids as the argument which is a bit cumbersome and not very intuitive when used outside of a form submission implementation. I can easily work around this by checking types inside the setter method but it feel hackery and not very Rails like.
I've tried ditching the whole custom getter/setter and going with a HABTM implementation that uses a join table to manage all these records but I am concerned that this won't scale well if/when we need to add more roles to the project (each role adds an extra column to the join table). It also ends up feeling like I am duplicating functionality/concepts that are already offered with Rolify so in some places I am checking for roles in a join table and in using Rolify in others (i.e. if a user is an admin or has access to a certain resource).
Is there something else I may have overlooked or this the only way of getting this done?
Thanks and I look forward to hearing some of your opinions.
Rog

Alternative to Rails Single Table Inheritance (STI)?

I have a model and table that I believe is perfectly suited to STI. My table is called Finances and has two types: Income and Expenses. Besides type there are three other columns: description, amount, and date.
I'm getting very nervous using STI in Rails, since it requires some hacking. I'm too new to Rails to hack up the code. Even though it works, I don't understand it. That seems dangerous.
My question is, how do I set up my model, controller, and view if I do NOT use STI? Any best practices to group items in my model? Or do I just do Finances.where("type = 'Income'") before setting up a view?
Edit: I made a gist to show the code I'm working with. When I run it I get the error:
undefined method `incomes_path' for #<#<Class:0x007fbc95f60b40>:0x007fbc93883220>
First, using STI is standard for Rails, so there is no need to feel nervous. And no need for "hacking".
It has been used very successfully by many developers. And as you have seen, you can find tutorials and general information on the net.
If on the other hand, you decide NOT to use STI, you have the choice of using
(a) completely separate models with their own tables, which will result in a lot of duplicated code, or
(b) create you custom "STI-like" behaviour by hand.
The second option could at least be interesting to learn more about Rails.
For example, in your Finances model you would define a scope incomes, like
scope :incomes, where(:type => 'Income')
then you can do Finances.incomes.
Then, if you have methods that apply only to one of the types, you should check that all records effectively are of the needed type.
Personally, I would advice you to use STI. You get a lot of functionality for free and you are doing it the Rails way.
Imagine, for example, other developers reading your code, they will ask themselves, why you didn't use STI, blame it on ignorance and - if need be - refactor it using STI.
STI is best if you are using inheritance structure like this. You don't really need to use Finances.where("type = 'Income'"). You can simply use Income.all. see these posts if they help you.
http://www.therailworld.com/posts/18-Single-Table-Inheritance-with-Rails
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/

RoR: How to use routes to filter refinerycms backend content?

everyone. I'm new to refineryCMS( and rails also). I summed up my question in the end of this article in case the problem description is too long.
Here is my question:
How do I use routes( or namespace) to filter backend contents?
For example, I have several departments on my site. They have similar structures, yet the contents are different. I want to manage them separately. Say, I have department ABC and department EFG and department MAIN, I want to manage them through /refinery/ABC and /refinery/EFG and /refinery(/MAIN).
How can I achieve something like this?
Currently I've two ideas, but not knowing how to implement.
First, I may create each department as rails engine and then mount them on the main_app. However, I don't like this solution because it will duplicate many almost identical tables since each department's structure are quite similar. And I'm not familiar with creating engine, worrying about the performance.
Second way I've thought out is to make every Refinery::Pages, resource and custom engine model belongs to certain department. And then I could write a controller to filter all those thing out by specifying which department like I mentioned above "/refinery/:department".
In short, I prefer the second way, though I don't know how to:
make every model(custom engine or Refinery native ones) belongs to department. Especially for Refinery native ones(resource, image, pages)
how to duplicate the admin rendering like refinery's default backend after controller has filtered content I want.
After some search up, I found that there are too many #variables to filter properly. Is there some easier way?
Thanks in advance!
Routes.rb
try put the:
get 'refinery/:dep' => 'refinery#deps'
then in refinery controller your put
def deps
dep = params[:dep]
*some code*
end

Resources