Rails form builder - how to improve performance - ruby-on-rails-3

I have a page that has several forms and the load time of the form and fields seems like it is way slower than it should be.
My form looks something like this
-#leaders = Leader.select("id, full_name")
-#managers = Leader.select("id, full_name")
=form_for organization, remote: true do |f|
=f.fields_for :projects, [project] do |project_fields|
Status:
=project_fields.collection_select :status_id, Status.all, :id, :title
Current Manager
=project_fields.fields_for :project_managers, [pm] do |project_manager_fields|
-# 50+ entries
=project_manager_fields.collection_select :manager_id, #managers, :id, :full_name
Current Lead
=project_fields.fields_for :project_leads, [pl] do |project_lead_fields|
-# 50+ entries
=project_lead_fields.collection_select :lead_id, #leaders, :id, :full_name
This is just a contrived example. I have everything preloaded so fields_for doesn't hit the database. This made a huge difference but its still too slow. I also tried caching the content of each for which made the speed acceptable but I don't think caching is the intended solution for this.
Is there anything else I can do to get this to render faster?
ruby 1.9.2 or 1.9.3, Rails 3.2.6, sqlserver adapter (everything is even slower with mysql), Ubuntu 10.04 VM (hosted in windows 2k3), 4gb, 3ghz xeon cpu
This is slow in development and production on mongrel/passenger, other pages load acceptably fast.

Recently, while optimizing a Rails app for performance, I found that some of the Rails form helpers are surprisingly slow. I was able to multiply rendering speed for several heavily used pages by replacing form helper calls with raw string interpolation.
To get the HTML generated by a helper, open up the page in Chrome, press F12 to open the Dev Tools, go into Elements, right click on the element, and choose "Copy HTML". Then copy the raw HTML into your template, and use #{} string interpolation as necessary to insert values in the right places. For the authenticity token <input>, use value="#{form_authenticity_token}".
This is ugly, some might even say hideous, but it's also FAST. By applying this change in some critical places, together with optimization of DB indexes and a few other changes, I was able to make the above-mentioned app handle 3.6x as much load on the same server!

Seeing there are so many forms, I guess the page itself would be too long to fit a screen. The user definitely can't handle so much information in short time.
So, if possible, why not load the necessary divs only at first? Then you can use toggle to display form on need by Ajax.

What about only using the necessary fields of the model?
=form_for organization, remote: true do |f|
=f.fields_for :projects, [project] do |project_fields|
Status:
=project_fields.collection_select :status_id, Status.find(:all,:select=>'id,title'), :id, :title
Current Manager
=project_fields.fields_for :project_managers, [pm] do |project_manager_fields|
-# 50+ entries
=project_manager_fields.collection_select :manager_id, Manager.find(:all,:select=>'id,full_name'), :id, :full_name
Current Lead
=project_fields.fields_for :project_leads, [pl] do |project_lead_fields|
-# 50+ entries
=project_lead_fields.collection_select :lead_id, Leader.find(:all,:select=>'id,full_name'), :id, :full_name

Related

Rails 5: form_for vs form_with

Rails 5 has introduced new form helper method form_with.
How does it differs with form_for and when is it more appropriate to use?
This is really in preparation for rails 5.1 where only form_with should be used. It's meant to serve as a replacement for the two methods form_for and form_tag.
form_for and form_tag in Rails were very similar, both allowed you to create a form tag but the first one uses model’s attributes to build create or update form, while the second one simply creates an HTML form tag with the passed URL as action.
Use form_with (more up-to-date)
form_with is the latest.
form_for and form_tag are obsolete.
Why is there a change?
See Kasper Timm Hansen's pull request - I cannot state it better than pull request itself:
form_tag and form_for serve very similar use cases. This
PR unifies that usage such that form_with can output just
the opening form tag akin to form_tag and can just work with
a url, for instance.
This means you don't need to use form_tag if you don't have a model. You can use the form_with helper and it can still handle URLs.
Note Re: Ids and Classes: The Ruby 5.1 version of form_with, by default doesn't attach a class or id to the form. For Rails 5.2 and later, however, form_with WILL automatically generate ids based on the model, if present—basically the same id behavior as form_for, so you will NOT need to manually specify them anymore. (source)
Existing answers are great. What I also found helpful were the first few paragraphs here. Basically:
form_tag and form_for are soft deprecated and they will be replaced by form_with in the future.
DHH's proposal: https://github.com/rails/rails/issues/25197
The pull request: https://github.com/rails/rails/pull/26976/files (I didn't read it, but it could be useful)

Basic nested routing in Rails 3

NOTE: I have read Routing From the Inside Out AND the Engine Yard blog post on routing.
I'm building a fantasy sports league, I have a League model that supports the seven basic restful operations, and they all work fine.
I've added the following my routes.rb
resources :leagues do
member do
get :invite
post :sendem
end
Later in the file I have a resources :leagues for the basic actions
and when I rake routes I can see:
invite_league GET /leagues/:id/invite(.:format) {:action=>"invite", :controller=>"leagues"}
sendem_league POST /leagues/:id/sendem(.:format) {:action=>"sendem", :controller=>"leagues"}
which is what I would expect. I have two functions in the League controller: "invite" which creates the form for collecting email addresses, and "sendem" which invokes a mailer to actually send the invitations.
The get for /league/:id/invite does indeed produce the form.
The form tag I am using looks like:
<%= form_tag(sendem_league_path, :method => "post") do %>
and yet the HTML that is rendered looks like:
<form accept-charset="UTF-8" action="/leagues/1" class="edit_league" id="edit_league_1" method="post">
And hence on submit generates a PUT which is completely wrong. (It should post to the sendem function.) My change to the routes file appears above the generic resources :leagues line, so it should have a higher priority.
I'm sure there is something dead-simple that I missed but I'm out of ideas. (And hair).
You should not use form_tag for manipulating resources.
You should use form_for.
Check out form helper guide - section 2 "Dealing with Model Objects". It takes care of deducing whether to use POST or PUT for a model object. For example, if your model object is new, it will use post on "resources"'s URL. if it is already existing database entity, it will use PUT to that "resource"'s URL.
ARGH some form handling error code at the top (form for #league) created a second form on the page for editing.... (left out of code snippets above for brevity). Original code seems to work as expected with that other code commented out. Thanks to vladdruzh for convincing me I was on the right track and to Salil for making me think to read the rendered HTML top to bottom.

Rails Routing with Dynamic Segments

So I just started using Dynamic segments as I need them to specify certain elements for grabbing data from AWS S3 via HTTParty.
I have this match statement in my routes.rb file:
match ':installation/:venue/:controller(/:action(/:id))'
This works great and allows me to do exactly what I want to do, which is pull in the installation and venue and use them with HTTParty to get their corresponding information from S3.
Now I need to keep my links through out persistent like these due to the fact that my application controller reads these in. So for example when I write a link_to I have had to do the following in a view/partial:
<%= link_to some_name,
"#{#installation}/#{#venue}/#{controller.controller_name}/show/some_id" %>
If it was just this ugliness I had to deal with that wouldn't be a problem, but I don't understand how I can pass around options in regards to this.
So basically is there a way to have resourceful routes for dynamic segments?
You can use polimorphic_url
http://apidock.com/rails/ActionDispatch/Routing/PolymorphicRoutes/polymorphic_url

rails 3.1.1 engines - with mountable engines, is it possible to access parent app assets, default layout?

This is more for experimentation - I am aware that I can do this with --full but I wanted the functionality of namespacing in the app to avoid conflicts
The idea is to have a main app - which handles authentication, common items, admin screens etc
Then creating engines to add further functionality like
crm
cms
blog
wiki
forum
etc
These engines I can pick and choose as I need for whatever kind of app I am building.
Is this possible?
Is it just the case of applying both --mountable and --full options?
Experimenting - would there be any issue if I use the full option add rspec and then simple add
rails plugin new plugin_name --skip-test-unit --full --dummy-path=spec/dummy
and in lib\plugin_name\engine.rb
module PluginName
class Engine < Rails::Engine
# this is added by rails when an engine is mountable
# to isolate the plugin and prevent name clashes
isolate_namespace PluginName
# one of the additions to make rspec work from command line for the plugin
config.generators do |g|
g.test_framework :rspec, :view_specs => false
end
end
end
I have already created both --full and --mountable engines and have rspec finally working for anyone reading there are some great articles (see below), but wondered of the wider impact of doing this for the solution I am trying to create
I am still playing with this and will post my findings..
Any help/discussion will be massively appreciated.
Please Note
Why I want to do it - build once use many times...
I would never want a non-tech/client to add "plugins/engines" - this is purely to entertain point 1.
Issues I am Having...
Running the server on the top level app. Only when accessing content from the engine, (I can see by the error messages) I have a routing problem (root_path undefined or devise routes missing) - the parent application layout is being rendered, I can see it in the extracted source of the error. Progress but no cigar just yet!
Useful References
Engines vs Mountable Apps
3.1 engines with rspec
testing rails 3 engines
Listing Routes in a Mountable engine
I managed to get this working with the following steps:
In my parent app I was mounting the engine in routes.rb
mount PluginName::Engine => '/plugin_name'
I just removed it.
Created an application controller as Ryan Bigg below had stated.
class PluginName::ApplicationController < ApplicationController
...
end
As I wanted to have things name spaced when generating controllers, models, tests so you have to essentially comment out the isolate_namespace PluginName lib\plugin_name\engine.rb when I wanted the gem to be run in the parent app.
It is not yet an ideal solution. off the top off my head, I could use something like:
isolate_namespace PluginName if %w[development testing].include?(Rails.env)
but will have to test if this is practical.
Kudos to Ryan for helping me find my way many thanks
Furthermore, the same can be done with the --mountable switch version and all you need to do is one further step in your engines config/routes.rb replace
PluginName::Engine.routes.draw do
with
Rails.application.routes.draw do
Yes, you can reference the parent application assets just by referencing them in your application like normal:
<%= stylesheet_link_tag "application %>
Although, not sure why you would want to do that because...
I'm going to answer your first question with the answer to your second question.
To use the application's layout you will need to modify the ApplicationController in the engine (which is namespaced) and have it inherit from ApplicationController in the engine.
That will then have the controllers for the engine using the layout provided by the engine. I'm doing this in my engine, forem.
One day, this will be covered in the Engines Guide that, at this time of writing, is currently being written.

rails3, params[:id] encryption

I trying to prevent url hacking, I passing an id to the url that the forms need, it works fine but if the user changes that value on the url it will send values to the wrong table.
<%= link_to '+ New Event',
{:controller =>'events', :action =>
'new', :company_id => company.id} %>
On the php world I used to encrypt that id ...how can I do this on rails3 or is there a better way ??
needless to say I sort of new to rails and I know a little bit of php
any help or suggestions will be greatly appreciated.
Even though this is an older question, it's a very worthwhile question. It is absolutely worthwhile to conceal the ID in the URL for, among other things, prevention of information disclosure.
For example, an application has a robust security model allowing users to only view resources to which they have rights. However, why should a user be able to look at the value of the ID in the URL and use it to deduce how many resources there are or, as the original questioner suggests, start trying to poke around with forced browsing.
The solution to this in rails turns out to be pretty simple. What I find works best is overriding to_param in the models, usually via a module in the lib directory and a before_filter in the application controller that decrypts the IDs.
For a walkthrough, have a look at this: http://www.youtube.com/watch?v=UW_s9ejrCsI
Rather than trying to encrypt or hide your company.id value, ask yourself what exactly it is that you want to prevent users from doing.
If you just want to prevent users from creating events associated with non-existant companies (by setting the id to a really high value for instance), then a simple
validates_presence_of :company
On the Event model would be fine.
If you only want users to be able to create events associated with companies that they work for, or have access for in some way, then you should create custom validations to verify that.
F