I'm not sure if I totally understand your problem but would a wizard help? There's a great talk about wizards here: http://confreaks.tv/videos/railsconf2014-ultra-light-and-maintainable-rails-wizards and follow-up blog post here: http://www.codepainter.ca/2013/10/ultra-light-maintainable-wizards-in.html
This approach uses a nice way to split out your models into sub-models so you can validate your form data at each step.
Something like this may work?
<%= f.label :name %> <%= f.collection_check_boxes(:site_ids, Sites.all, :id, :name) %>
Plus another way on Stack Overflow:
Posted in Advice on building a 'reports' feature
Not sure if this helps but this blog post may be a way to go:
I've never implemented something like this myself...but it seems like what you're describing is some kind of dashboard, with data pulled from a lot of different models.
Just wanted weigh in on this. I've also been building a multi-tenant application and it's lead me down some interesting paths… I read Ryan Bigg’s book on the topic (“Multitenancy with Rails”) but decided to figure out my own solution in the end. The book strongly recommends against going the postgres schemas route—mostly because of performance issues. From my understanding it’s like having to host multiple rails apps for each user, at the same time, on your production server. Not so bad if you only have a couple users, but it can get pretty expensive as you scale up.
Ryan’s solution is to roll your own authentication system using Warden and use subdomains to scope your data. It all sounded pretty good but one of the problems with the book (at least for me) was that Ryan decided to write all of this stuff inside of a Rails engine which just added to a lot of confusion for me. As I was going through the book I decided not to do the engine approach and just use a starndard rails app. Long story short I completely botched the job! I couldn’t get the authentication / subdomains to work in tandem and I ended up with more confusion than anything. Complete failure!
In the end I decided to make my own solution using Devise for authentication (because…well…it’s just easy!), NO subdomains, and use Pundit to help scope the data.
How it works is pretty simple:
In Pundit all you have to do is make sure your user is scoping to the model of your choice. I eventually want to add multiple users that have access to the same data so I ended scoping everything to the Company model. (A User has_one :company, a Company has_many :users).
Pundit setup is standard except I added this to the application_policy:
class Scope attr_reader :user, :scope def initialize(user, scope) @user = user @scope = scope end def resolve scope.where(company_id: user.company.id) end end
Then in your controllers all you need to do scope your data is use the pundit method policy_scope. That way everything is scoped to the company:
def index @plans = policy_scope(Plan).all authorize @plans end
That's it! As you add models to to your app you just need to make sure they are all tied to the company. (belongs_to :company).
I like this solution because it’s simple. You can use Devise out of the box and you don’t have to go through the hassle of dealing with subdomains. Also I like the fact that Pundit has my back and will throw an error if I forget to scope something properly.
A couple gotcha’s though:
When scoping data you need to make double sure that your scopes are working and you’re not accidentally showing the wrong data to the wrong user. Good tests here are essential.
The routes can be a bit tricky because ideally you don’t want to have any ids in your web addresses. Example: You don’t want someone manually typing http://yourapp.com/client/3 into their browser if the third client in your database belongs to someone else. Mostly I’ve been able to avoid this by using a lot of resource (singular) :model in the routes. So far that seems to be working but I think I’m going to have to break that pretty soon. I’ll probably have to generate a GUID and use that in the routes instead of a regular id.
I’m still developing this app but so far it seems like a pretty good solution for me. I’m still super new to all this though so please let me know if this is a horrible idea!
Brilliant! Got it working. More info here too if anyone's interested: https://github.com/intridea...
Super helpful episode! I was in the middle of connecting my app to Stripe Connect (which also uses OmniAuth) and the steps are pretty much identical. Just wondering if anyone has come up with a good strategy for testing this kind of behaviour?