Sinatra App Not Using Erb in Links - ruby

The problem I'm having is that Sinatra apps don't always seem to show my ERB when it is used in some specific context of hyperlinks. I have no idea how much is relevant to show for this question, so I'll do the best I can .Basically, I have a file in my Sinatra app called book_show.erb. It has the following line:
<p>Edit Book</p>
However, when that link is rendered in the browser, it links like this:
http://localhost:9292/books/#{#book.id}/edit
The #{#book.id} was not replaced with the actual ID value. There is a #book object and I do use it in other contexts in that very same file. For example:
<h1><%= #book.series %></h2>
<h2><%= #book.title %></h2>
<h3>Timeframe:</h3>
<p><%= #book.timeframe %></p>
I don't even know if my routes would make a difference here for diagnosing this but all of the relevant routes for my books functionality are:
get '/books' do
#title = 'Book Database'
#books = Book.all
erb :books
end
get '/books/new' do
#book = Book.new
erb :book_add
end
get '/books/:id' do
#book = Book.get(params[:id])
erb :book_show
end
post '/books' do
book = Book.create(params[:book])
redirect to("/books/#{book.id}")
end
I don't know what else to show to help diagnose this problem. I'm hoping someone sees something terribly obvious that I'm missing.
Adding a book to the database works just fine; it's only that edit link that I can't get to work correctly -- and that would seem to be pure HTML/ERB. In order to test it, I also added this line to the page:
<p>Testing: <%= "/books/#{#book.id}/edit" %>
That came back and returned this text:
Testing: /books/4/edit
So I know the ID is getting stored. It has to be something to do with the hyperlink but I can find nothing useful on Sinatra that helps at all with this.

ERB template does not behave like a ruby string - you need to explicitly tell it you exit the 'template' part into the 'logic' part. It looks very odd when it comes to attributes:
<p>Edit Book</p>
You could use link_to helpers to make it look better:
link_to('Edit Book', controller: 'books', action: 'edit', id: #book.id)

Related

Ruby/Sinatra: Passing a URL variable to an .erb template

I'm using Padrino, and I want to take parameters out of URL and use them in an .erb template.
In my app setup I have:
get '/testpage/:id' do
userID = params[:id]
render 'test/index'
end
In my test/ folder I have index.html.erb which is successfully rendered, for a url like http://localhost:9000/testpage/hello123.
However, I've tried printing the params[:userID] on the page with:
<%= #userID %>
The rest of the page renders fine but hello123 isn't anywhere to be found. When I try <%= userID %> I get undefined local variable or method `userID' for #<stuff>
What am I missing here?
Just a guess, because I've never used Padrino, but if it works like Rails this may help you:
get '/testpage/:id' do
#userID = params[:id]
render 'test/index'
end
In sinatra, it's just like this (see "Views/Templates" section):
get '/testpage/:id' do |id|
erb :index, :locals => {:id => id}
end
The template is located in views/index.erb by default. It could be change.

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.

How to correct in the Sinatra show block

Sorry, I will not use the specific expression in English.
index.erb
<h1>Hello World.</h1>
<ul>
<li>item1</li>
<li>item2</li>
</ul>
<% capture_content :key do %>
I'm Here.
<% end %>
helpers
def capture_content(key, &block)
#content_hash = {}
#content_hash[key] = block.call # this block contains erb all
end
I just want capture_content in content
I hope expression is correct T_T
If you are looking to write yourself the Sinatra equivalent of the Reails content_for helper then you don't need to.
Because there is an extension called Sinatra::ContentFor which is part of the Sinatra::Contrib project which does what you want.
From the documentation:
Sinatra::ContentFor is a set of helpers that allows you to capture
blocks inside views to be rendered later during the request. The most
common use is to populate different parts of your layout from your
view.

Using Padrino form helpers and formbuilder - getting started

I've jumped into learning Ruby by going straight to Padrino with Haml.
Most of the Padrino documentation assumes a high-level of knowledge of Ruby/Sinatra etc...
I am looking for samples that I can browse to see how things work. One specific scenario is doing a simple form. On my main (index) page I want a "sign up" edit box with button.
#app.rb
...
get :index, :map => "/" do
#user = "test"
haml: index
end
get :signup, :map => "/signup" do
render :haml, "%p email:" + params[:email]
end
...
In my view:
#index.haml
...
#signup
-form_for #user, '/signup', :id => 'signup' do |f|
= f.text_field_block :email
= f.submit_block "Sign up!", :class => 'button'
...
This does not work. The render in (/signup) never does anything.
Note, I know that I need to define my model etc...; but I'm building to to that in my learning.
Instead of just telling me what I'm doing wrong here, what I'd really like is a fairly complete Padrino sample app that uses forms (the blog sample only covers a small part of Padrino's surface area).
Where can I find tons of great Padrino samples? :-)
EDIT
The answer below was helpful in pointing me at more samples. But I'm still not finding any joy with what's wrong with my code above.
I've changed this slightly in my hacking and I'm still not getting the :email param passed correctly:
#index.haml
...
#signup
- form_for :User, url(:signup, :create), :method => 'post' do |f|
= f.text_field_block :email
= f.submit_block "Sign up!"
...
#signup.rb
...
post :create do
#user = User.new(params[:email])
...
end
EDIT Added Model:
#user.rb
class User
include DataMapper::Resource
property :id, Serial
property :name, String
property :email, String
...
end
When this runs, params[:email] is always nil. I've compared this to bunches of other samples and I can't see what the heck I'm doing wrong. Help!
You can browse some example sites here: https://github.com/padrino/padrino-framework/wiki/Projects-using-Padrino
Or you can browse sources of padrinorb.com here: https://github.com/padrino/padrino-web
The best way also is to generate admin: padrino g admin where you should see how forms works.
The tag form perform by default post actions unless you specify :method => :get|:put|:delete so in your controller you must change :get into :post
post :signup, :map => "/signup" do ...
Since you are using form_for :user params are in params[:user] so to get email you need to puts params[:user][:email]

Rails calling action from view

Hopefully have a simple question here but I cannot for the life of me seem to find the answer. Just started working with RoR but came from ASP MVC before. I am having an issue rendering partial views whose local variables are not necessarily tied to the variables of the main view. For instance, with a blog I am trying to render a sidebar that will link to the archive.
def sidebar
#blog_posts = Blog.all(:select => "created_at")
#post_months = #blog_posts.group_by { |m| m.created_at.beginning_of_month }
end
The partial view _sidebar is as follows:
<div class="archives">
<h4>Blog Archive</h4>
<% #post_months.sort.reverse.each do |month, posts| %>
<%= link_to "#{h month.strftime("%B %Y")}: #{posts.count}", archive_path(:timeframe => month) %>
<% end %>
</div>
The problem I am having is that if I simply do a render 'sidebar' within my main view the action does not seem to be called and #post_months is always nil. Is it possible to call the action directly from the view and simply have that render 'sidebar'? In ASP MVC I used to just make the sidebar a ChildActionOnly and Render.Action from the mainview, but in RoR I am completely clueless. Any help is appreciated!
I think what's happening here is that yout sidebar is being treated as a partial and your controller method is never being called. In that case I'd put the code currently contained in the sidebar controller method into either the ApplicationHelper module or the helper module of the current view, depending on whether or not you'd need to render the sidebar from other views.
You'd need to adapt the code a bit to work in a module. Rather than setting a session variable you should have the methods return the values you want.
Module SomeModule
def blog_posts
Blog.all :select => "created_at"
end
def post_months
blog_posts.group_by { |m| m.created_at.beginning_of_month }
end
end
Of course, that may very well need to be refactored and might not work as written, but that's the general idea I'd go with.
Good Luck.

Resources