Get template string from file and automatically bind data - ruby-on-rails

I'm implementing SmsService to send text message to user's phone. The SmsService receives the text message content from SmsContent:
class SmsContent
def initialize(sms_type, data)
#sms_type = sms_type
#data = data
end
def get_content
sms_template_one_way = 'Hello #{customer_name}, this is your one way itinerary from #{depart_place} to #{arrive_place} ...'
sms_template_round_trip = 'Hello #{customer_name}, this is your round trip itinerary from #{depart_place} to #{arrive_place} ...'
customer_name = #data.customer_name
depart_place = #data.depart_place
arrive_place = #data.arrive_place
if #sms_type == 1
sms_content = sms_template_round_trip
else
sms_content = sms_template_one_way
end
sms_content
end
end
I have to store my message template as String variables. How can I refactor this code? Specifically, how can I store message templates in a file and automatically bind data to template?

class SmsContent
def initialize(sms_type, data); #sms_type, #data = sms_type, data end
def get_content
"Hello %{customer_name}, this is your %{sms_type} itinerary "\
"from %{depart_place} to %{arrive_place} ..." %
{
sms_type: #sms_type == 1 ? "round trip" : "one way",
customer_name: #data.customer_name,
depart_place: #data.depart_place,
arrive_place: #data.arrive_place,
}
end
end

First make a partial file under app/views/sms_content/_get_content.html.erb
_get_content.html.erb
<% if #sms_type == 1 %>
Hello <%= #data.customer_name %>, this is your one way itinerary from <%= #data.depart_place %> to <%= #data.arrive_place %> ...
<% else %>
Hello <%= #data.customer_name %>, this is your round trip itinerary from <%= #data.depart_place %> to <%= #data.arrive_place %> ...
<% end %>
In your SmsContent class get_content method
def get_content
render :partial => "sms_content/get_content"
end
This should work

As I indicated in my comment above, Rails' built-in I18n API is practically designed for this exact thing. First, store your template strings in the config/locales/ directory:
# config/locales/en.yml
en:
sms_template:
one_way:
Hello %{customer_name}, this is your one way itinerary
from %{depart_place} to %{arrive_place} ...
round_trip:
Hello %{customer_name}, this is your round trip itinerary
from %{depart_place} to %{arrive_place} ...
Note that this format uses %{...} a la sprintf for interpolation instead of #{...}.
Then:
def get_content
template_name = #sms_type == 1 ? "round_trip" : "one_way"
I18n.t("sms_template.#{template_name}", #data.attributes)
end
That's it! One thing to note: The above assumes that #data is an ActiveModel object (or something that responds to attributes with a Hash); if that's not the case you'll have to build a Hash first.

Related

Rails: How to pass params of multiple checkbox to the model

i built this form that generate me some chebox with value like "U6", "U8" eccc
<%= form.label "Seleziona Categorie" %>
<% TeamCategory::NAMES.each do |category| %>
<%= check_box_tag 'categories_selected[]', category -%>
<% end %>
Now i have to pass the value of selected check_box to a method in my model.
Now is:
def create_tournament_team_categories
TeamCategory::NAMES.each do |name|
team_category = TeamCategory.where(name: name).first_or_create
self.tournament_team_categories << TournamentTeamCategory.create(team_category: team_category)
end
end
I would like to replace the TeamCategory::NAMES.each do with "selected check_box each do" and TeamCategory.where(name: name) with the value selected.
Thank you in advance
I am a newbie with Rails. What I see is that you took the part of the form to create the team, right?
For your code straight forward it could be:
<%= form.label "Seleziona Categorie" %>
<% TeamCategory::NAMES.each do |name| %> #you are looping through team category NAMES constant
<%= check_box_tag 'category_names_selected[]', name %>
<% end %>
Your form as is allows more than one category to be selected.
For the method:
def create_tournament_team_categories(category_names_selected)
category_names_selected.each do |name|
team_category = name
self.tournament_team_categories << TournamentTeamCategory.create(team_category: team_category)
end
end
you will probably use this method in your teams_controller.rb. In the controller, you should be able to retrieve from params a freshly created array of selected names with something along the lines with this.
#category_names_selected = params[:category_names_selected]
I do not know how complicated your app is so it might also be nested under ["team"][:category_names_selected] or ["team"]["category_names_selected"] in your params hash.
To see the exact structure of the params hash and adjust the equation above you can add for example require 'pry' at the top of your controller file and then but the binding.pry just after the part where your method is executed. When you restart the server and the app hits this part of the controller you should be able to see the exact structure of your params hash in the terminal.
You can then pass the array to the method that you can call in the controller. Do not forget to add :category_names_selected to the strong params in the controller. I hope this helps.
Controller on line 30
def create
#tournament = Tournament.new(tournament_params)
#tournament.sport_club = current_user.sport_club
#category_names_selected = params[:category_names_selected]
if #tournament.save
redirect_to tournaments_path, notice: 'Torneo creato con successo'
end
end
Method create_tournament_team_categories in the model
after_create :create_tournament_team_categories
def create_tournament_team_categories(category_names_selected)
#category_names_selected.each do |name|
team_category = name
self.tournament_team_categories << TournamentTeamCategory.create(team_category: team_category)
end
end

Rails print user input and tag at the same time

I would like to print with <%= %>. PS: the following works but looks bad.
<%= "String: #{html_escape #user.input} <br>".html_safe unless #user.input.blank? %>
I know I can do
<% unless #user.input.blank? %>
String: <%= #user.input %><br>
<% end %>
Is it possible to write a method that looks like that:
def print_if_not_blank (string, input)
string.insert(html_escape input).html_safe unless input.blank?
end
printf_if_not_blank "String: #{} <br>", #user.input
I guess I could always have a param like "String: EVIL < br>" and than replace "EVIL" but that's not better than my current solution.
You could write a helper like this:
def print_if_present(template, input)
return unless input.present?
string = template % { input: html_escape(input) }
string.html_safe
end
What is used like this in your view:
<%= print_if_present('String: #{input} <br>', #user.input)

Rake task to create a Rails partial from json data

Right now I have a helper:
def blog_posts
Rails.cache.fetch("blog_posts", :expires_in => 30.minutes) do
url = URI('http://www.xxxxxxxx.com/blog/?json=get_recent_posts')
request = Net::HTTP.get(url)
response = JSON.parse(request)
response['posts'].take(4)
end
rescue => e
logger.error "Error retrieving blog posts!"
logger.error "#{e.message}\n#{e.backtrace.join("\n")}"
[]
end
And the view I have:
<% blog_posts.each do |blog| %>
<div class="col col-3">
<a class="search-page-links" href="<%= blog['url'] %>" target="_blank">
<img class="post-image" src="<%= blog['thumbnail'] %>">
<br/><br/>
<%= blog['title'].html_safe %>
</a>
</div>
<% end %>
Which works fine. Except we dont want the data to be pulled when the page loads at all anymore, because we cant always count on the source. The plan is to add a Rake task to the deploy process so that static data can be updated in a rails partial at each deploy, which would be rendered into the view. How could I go about doing this?
Creating partials just sound like the worst possible solution because you are creating a huge amount of code duplication, and potentially a huge slug (deployed code base). If you have to then save the JSON data to disk and use fragment caching if needed.
Other alternatives would be to use a key/value or JSON based storage instead of storing the data in your codebase (which is almost always a bad idea).
I would sit down with the client or whoever is making up the rules and explain that it would be a pretty big maintenance problem down the line and suggest a solution that is actually tenable.
What I did
Rake Task:
namespace :blog do
task :pull do
tmp = "tmp/blog_posts"
def fetch_blog_posts
json = open('http://www.xxxxx.com/blog/?json=get_recent_posts').read
hash = JSON.parse(json)
hash['posts'].take(4)
end
def download_blog_images
fetch_blog_posts.each_with_index do |blog, index|
File.open("tmp/#{index}.jpg", 'wb') do |f|
f.write open("#{blog['thumbnail']}").read
end
puts "Downloaded #{(index.to_i+1).ordinalize} Image"
end
end
def retrieve_blog_data
puts 'Retrieving blog data from Wordpress'
fetch_blog_posts
File.open("tmp/blog_posts.json", "w+") do |f|
f.write(JSON.pretty_generate(fetch_blog_posts))
puts 'Saved json data'
end
download_blog_images
end
retrieve_blog_data
puts 'Done retrieving blog posts'
end
end
Helper:
module BlogHelper
def blog_posts
if Rails.env.development?
json = open('http://xxxxx/blog/?json=get_recent_posts').read
else
json = File.read("tmp/blog_posts.json")
end
hash = JSON.parse(json)
hash['posts'].take(4)
end
end
Controller:
def home
#blog_posts = blog_posts
end
View:
<% #blog_posts.each_with_index do |blog, index| %>
# markup for each post...
<% end %>

Saving a string with erb data in the model

I currently save code like below in my user model.
User.where(type: "supercool").each do |user|
if user.score == 100
user.message = "Hello #{user.name}, you now have #{user.points} points!".html_safe
elsif user.address == "New York"
user.message = "You live in the same town as #{Person.where(address:"New York").first.name}"
user.save
end
I save these messages in my model, and print them out in the view by just calling User.find(params[:id]).messages in the controller.
The actual model code is much longer, as there are many unique messages based on different scenarios.
While this works, I would now like to add a link_to to objects that are mentioned in the messages.
For the first message, I want to save it so that when I send it to the view, it's in the form of
Hello <%= link_to user.name, user_path(user.id) %>, you now have <%= link_to user.points, leaderboard_path %> points!
I tried the below, but it doesn't work.
User.where(type: "supercool").each do |user|
if user.score == 100
user.message = "Hello #{link_to user.name, user}, you now have #{link_to user.points, leaderboard_path} points!".html_safe
user.save
end
How can I save erb in my string?
How about this? Use html tags instead of using link_to
User.where(type: "supercool").each do |user|
if user.score == 100
user.message = "Hello <a href=#{Rails.application.routes.url_helpers.user_path(user)}>#{user.name}</a>, you now have <a href=#{Rails.application.routes.url_helpers.leaderboard_path}>#{user.points}</a> points!".html_safe
user.save
end
end
And in your view, Just do
<%= user.message %>
This is not a very good pratice.
You can like:
class Model < ActiveRecord::Base
include Rails.application.routes.url_helpers
def example
ActionController::Base.helpers.link_to user.name, user
end
end
But using a helper or a erb template to show this to the user seems the best way.

How to capture the filename of the browse button?

I am new to ROR. I want to capture the filename of the browse button after selected the file with the extension. And i want to store the filename into the File_name column in the database. Please help me to do that. Is it possible to get the full path of the file stored in the File_name column instead of just a filename? please suggest me to do that. I dont want to upload the file. Just i want a filename to store.
View(New)
--------------
<div class="pro-data-imports">
<%= form_for #pro_data_import do %>
<div class="field">
Browse the file to upload:<br />
<%= file_field_tag :File_name %>
</div>
<div class="actions">
<%= submit_tag 'Import File' %>
</div>
<% end %>
</div>
Controller
----------
class Pro::DataImportsController < ApplicationController
before_filter :authenticate_user!
layout "layouts/enr/energy_master"
def index
#pro_data_imports = Pro::DataImport.all
end
def new
#pro_data_import = Pro::DataImport.new
#pro_data_import.updated_by = current_user
end
def create
#pro_data_import = Pro::DataImport.new(params[:pro_data_import])
#pro_data_import.updated_by = current_user
end
end
Model
-------
I have File_name, File_validate, :updated_by Columns
I don't know what you meant by that. In your comments you mentioned to want to know, how to call the store procedure with two parameters. If its your database is SQL try the following and change your field name as per your spec.
In your model, create a method
def update
parameters = [self.File_Name, File_Location]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "your store procedure name"(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
end
In your controller,
Inside your create method, call the update method.
def create
#update = Pro::DataImport.new(params[:pro_data_import])
#pro_data_import.updated_by = current_user
#update.update
.........
...
end
I hope it will help. This is just an example to follow. Do not copy and paste.. :)

Resources