Rails: How to receive non-model form input, convert it to a model attribute, and then save that model - ruby-on-rails-3.1

Background:
I have a form_for mapped to a model called List.
List has attributes: name, id, receiver_id, assigner_id.
I want the user( or list assigner) to be able to choose a list receiver.
I want the assigner to input an e-mail, rather than the receiver's id.
Problem:
I am not sure how to use a form to receive an e-mail address, run a "User.find_by_email(xx).id" query using that e-mail address, and then assign the returned id to the List's receiver_id attribute.
Current Code:
lists_conroller.rb
class ListsController < ApplicationController
before_filter :current_user
def new
#list = List.new
end
def create
#list = List.new(params[:list])
#list.assigner = #current_user
##list.receiver = User.find_by_id(:receiver_id)
#list.save
redirect_to #list
end
def show
#list = List.find(params[:id])
end
def update
#list = List.find(params[:id])
end
end
lists\new.html.erb
<%= form_for #list do |f| %>
<%= f.label :name, 'Name'%>
<%= f.text_field :name %>
<%= f.label :receiver_id, 'Receiver ID'%>
**I want this to be the e-mail input, rather than the integer id.**
<%= f.text_field :receiver_id %><br />
<%= f.submit :submit %>
<% end %>

User creates new list, with him as the assigner. In that creation process there must be a receiver too. Did I get this right?
I think the receiver should be selected from a list of possible receivers (maybe a select box? this will depend on the number of possible receivers though, wouldn't want to list 1000+ users in there - if there are many users you could do an ajax search when the user types a few letters)
The assigner then selects a user (with the corresponding id as the value) and everything should be ok.

The answer to my question is "Virtual Attributes..."

Related

Rails email preview for users in production

Context
Gems like mail_view, mailcatcher, rails_email_preview, etc. seem to be more developer-oriented (a way to debug a template). But I need something that will be used by the trusted users of my rails app in production.
My app is a project management app, where project managers can update the status of their projects, operations during which emails must be sent to project contractors, developers, clients, etc.
The project manager must be able to tell whether or not he wants to send an email (this is easy), and be able to customize to some extent the message content (not the design, only specific text parts should be enough). They DO want to have some control over the email about to be sent, ie, they need a preview of the email they customized. Why ?
Project Managers are trusted users/programmers, and I let them add HTML as custom parts of the email (We are talking about a small-scale app, and the project managers are all trusted employees). But a closing tag is easily forgotten, so I want to provide them with a mean to check that nothing is wrong. Eg. that the text does not all appear as <h2> just because they forgot a closing </h2>
Some email templates already include some info about what the PM is writing about, and the PM may not be aware of it (understand : may be too drunk to remember it). An email preview is just a way to avoid duplicate sentences (like two times Hello M. President,)
CSS styles are applied to the email. It can be hard to anticipate the effect of tags like <h2>, etc. So I need to render the email with the CSS
REMARKS
Previsualize & Send button
Project managers have access to a form that will feed the content to my Rails app. I am thinking on having both a normal submit button, and a previsualize button. I will probably use some tricks given by this SO question to differentiate the behaviours of the 2 buttons
Letter_opener : a nice gem, but exclusive ?
I am using letter_opener for debug (so different context), but this is typically the preview I'd like to show to the project manager. However, in order to be used, letter_opener requires to modify action_mailer configuration config.action_mailer.delivery_method = :sendmail # (or :letter_opener). So I can only previews emails, or send them for real, not both ? I would accept a solution that would let me choose whether to use letter_opener or send the email for real
Small Editor ?
Instead of blindly trusting my project managers' ability to write basic html without forgetting closing tag, maybe you could recommend a nice WYSIWYG editor that would show the content of my f.text_area() ?
This would be a bonus, not an actual answer to my question
Email rendering engine ?
I am now aware that different email clients can render the email client differently. I will ignore this for now. So the way the preview is rendered doesn't matter. I like the rendering of letter_opener however
Current Code
View > Controller > Mailer
my_email_view.html.erb
<%= form_tag some_mailing_list_path %>
<%= fields_for :email do |f| %>
<!-- f.text_field(:subject, ....), etc -->
<% end %>
<%= submit_tag("Send email") %>
<%= submit_tag("Preview") %>
<% end %>
my_controller.rb
before_action :prep_email # Strong parameters, define #mail with form contents
# Handles the POST
def some_action
check(:to, :from, :subject) # I check the parameters in private functions
if email_alright? # Above checks will raise a flag if something went wrong
if Rails.env.production?
MailingListsMailer.my_action(#mail).deliver_later
else
MailingListsMailer.my_action(#mail).deliver_now
end
flash.notice = "Email sent"
redirect_to :back
else
redirect_to :back
end
end
mailing_list_mailer.rb
def my_action(message)
format_mail_params(message) # Will set more variables
#etude = etude
#include_reference = message[:include_reference]
#include_description = message[:include_description]
dst = Proc.new { read_emails_file }
mail(
to: dst,
from: message[:from],
subject: #subject_full)
end
Question update: based on your pseudocode, this is a simple case of creating a status update model and emailing the update to a mailing list.
There are several ways you can go about it, but I'd suggest that you keep things simple and avoid using gems.
<%= link_to "New update", new_status_update_path, class: 'button' %>
Model
class StatusUpdate
belongs_to :sender, class_name: 'User'
belongs_to :mailing_list
end
Controller
class StatusUpdateController
def new
#status_update = StatusUpdate.new
end
def create
#status_update = StatusUpdate.create(status_update_params)
#status_update.mailing_list = MailingList.where(whichever_mailing_list)
if #status_update.save
redirect_to :action => "preview", :status_update => #status_update
end
end
def preview
#status_update = StatusUpdate.where(id: params[:id]).first
#mailing_list = MailingList.where(id: #status_update.mailing_list_id)
end
def send
#status_update = StatusUpdate.where(id:params[:status_update_id]).first
Mailer.status_update_email(#status_update).deliver
end
end
status_updates/new.html.erb
<%= simple_form_for(#status_update) do |f| %>
<%= f.input :title %>
<%= f.input :content, as: :text %>
<%= f.button :submit, 'Post update' %>
<% end %>
status_updates/preview.html.erb
<h1>Preview</h1>
<%= simple_form_for(#status_update, :url => url_for(:controller => 'StatusUpdateController, :action => 'send') do |f| %>
<%= f.input :subject %>
<div class="email-render-container">
<%= #status_update.content %>
</div>
<p>Make changes</p>
<%= f.input :content, as: :text %>
<%= f.button :submit, 'Approve and send emails' %>
<% end %>
If I were you, I'd do away with the preview feature. If you're
loading content from a template and all you're worried about are
potential duplicate content, just do this:
Controller
class StatusUpdateController
def new
#status_update = StatusUpdate.new
template = UpdateTemplate.where(however_you_assign_the_template)
#status_update.content = template.content
end
def create
#status_update = StatusUpdate.create(status_update_params)
#status_update.mailing_list = MailingList.where(whichever_mailing_list)
if #status_update.save
Mailer.status_update_email(#status_update).deliver
end
end
end
and style the new status update form with css to simulate writing on the actual email template. You'll save your users and yourself a lot of time.
wysiwyg editor
Never trust the end user with the ability to write html. Depending on your needs, I find https://www.froala.com/wysiwyg-editor easy to deploy.
differentiating buttons
Just use a preview icon with a label on your button and/or a subtitle under your button to differentiate your buttons. You don't need much command logic in your view.
Alternatively, if you think that the preview is important to your end users, just use the "preview" button as the next logical step instead of presenting your users with too many unnecessary choices.
Suggestions
Adopting a front end framework like Angularjs makes this sort of use case almost trivially easy, but it may be overkill and comes with steep learning curve if you're not familiar with it.
Take a look at letter_opener gem. It was created by Ryan Bates, the Railscasts guy.

Rails 4 search bar

I'm trying to create a search bar in my Rails 4 app. I'm my user db has 'name' and 'email' columns for the user's - I want users to be able to search for other users by name or id.
I'm currently getting this:
ActiveRecord::RecordNotFound in UsersController#index
Couldn't find all Users with 'id': (all, {:conditions=>["name LIKE ?", "%hi#example.com%"]}) (found 0 results, but was looking for 2)
Does anyone know what I'm doing wrong? I've looked at railscasts and a few forums etc but cant get past this point at the moment.
index.html.erb:
<% form_tag users_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
model/user.rb:
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
users_controller.rb:
def index
#users = User.search(params[:search])
end
routes.rb:
get 'search' => 'users#index'
Are you using Rails 4? find(:all, ...) is the old way of doing things. find only takes ids now. Use:
def self.search(search)
if search.present?
where('name LIKE ?', "%#{search}%")
else
where(true)
end
end
Also present? will test against both nil and blank. Keep in mind that LIKE can be really slow depending on your database.
Searching in rails
By default rails doesn't support full text searching .Activerecord finder always find a record using the primary key ie. id.
Hence , we need some other gems or applications like sunspot, elasticsearch etc..
i'll show you using Sunspot solr here ..
in the gem file of your application just add the following code...
.
gem 'sunspot_rails'
group :development do
gem 'sunspot_solr'
end
and in terminal use
bundle install
rails g sunspot_rails:install
this will add solr and creates config/sunspot.yml file
rake sunspot:solr:start
rake sunspot:reindex
edit the user.rb lile in models
app/model/user.rb
Class User < ActiveRecord::Base
searchable do
text :name, :email
end
end
in app/controller/users_controller.rb
and then add in index function
def index
#search = User.search do
fulltext params[:search]
end
#users = search.results
end
Make a form to take a user input
<%= label_tag(:search, "Search for:") %>
<%= text_field_tag(:search) %>
<%= submit_tag("Search") %>
<% end %>

Conditional HTML attribute

In ActionView I need to display an attribute based on a condition.
<%= f.text_field :regmax_remote, {
:class => 'span2',
:style => "display:#{#event.regmax_remote.present? ? "block" : "none"};"
}
%>
Is there a prettier way to go about this?
The above code is fine, If you are going to use it only once in the,
But If this will be used in many places then u may need helper
def event_display_style event
event.regmax_remote.present? ? "block" : "none"
end
if you have multiple attributes based on several conditions then u can use the helper to return the attributes in hash format and use it like this.
<%= f.text_field :regmax_remote, event_display_style(#event) %>
if u want a variable hash with default hash then u can do something like this as well
<%= f.text_field :regmax_remote, {class: "span2"}.merge(event_display_style(#event)) %>
There are some other ways to make this code look better. U may also like the draper gem. which gives an object oriented control over displaying at the same time can acce view helpers.
https://github.com/drapergem/draper
You can try like the following,
<% if (#event.regmax_remote.present?) %>
<%= f.text_field :regmax_remote, class: "span2" %>
<% end %>
Do not copy the same, just edit as per your code and use this as the example.

ActiveModel::MassAssignmentSecurity Error - concerning time_select in formtastic?

I'm using the following code to create some timeslect and date select objects using activeadmin and formtastic. The result generates two timeselects and a date select object as the correspoinding data field types are time and date. The problem is that when I submit the form I get a ActiveModel::MassAssignmentSecurity error
<%= f.inputs :class=>'inputs', :for=>Schedule.new do |fu| %>
<%= fu.input :eta, :label=>"ETA", :ignore_date=>true %>
<%= fu.input :etd, :label=>"ETD", :ignore_date=>true %>
<%= fu.input :date, :ignore_time=>true %>
<% end %>
When posting the etd and eta fields are posted as arrays - is there a way to get the actual values of them cleanly?
I can't know for sure what your problem is without seeing the models in question, but my best guess is that you've got an issue with accepts_nested_attributes_for and attr_accessible.
On the model represented by the parent form, you will need:
has_one :schedule #assuming it's has_one, could be something else...
accepts_nested_attributes_for :schedule
attr_accessible :schedule_details
Note that the association name in attr_accessible is appended with _details. If you just have the association name, Rails will throw an exception.
Does this look like it could be the issue?

Sorting and manipulating a hash in Ruby

In my rails 3.1 project I have a Book model that has a ID, NAME, and BOOK_ORDER. I am using the ranked-model gem, which for its sorting process creates large numbers in the sorting(:book_order) column. Im looking for some help to create a method to sort all of the Books by the :book_order column, then simplify the :book_order numbers.
So, I have this:
controller
#books = Books.all
view
<% #books.each do |book| %>
<%= book.book_order %>
<% end %>
# book1.book_order => 1231654
# book2.book_order => 9255654
# book3.book_order => 1654
But want this:
view
<% #books.each do |book| %>
<%= book.clean_book_order %>
<% end %>
# book1.clean_book_order => 2
# book2.clean_book_order => 3
# book3.clean_book_order => 1
Additionally, i don’t want to change the database entry, just use its current values to make simpler ones.
Thanks!
UPDATE:
Thanks to nash’s response I was able to find a solution:
In my Book Model I added the clean_book_order method:
class Book < ActiveRecord::Base
include RankedModel
ranks :book_order
def clean_book_order
self.class.where("book_order < ?", book_order).count + 1
end
end
<% #books.each do |book| %>
<%= book.book_order_position %>
<% end %>
EDIT:
Oh, I see. https://github.com/harvesthq/ranked-model/issues/10

Resources