Take control over email subject in rails - ruby-on-rails

I need to change the subject of the emails on a low level.
What rails does is encoding the subject as quoted in whatever encoding is set.
What I need is to make it quoted but split into chunks of 64 byte, as hotmail doesn't really goes with the standards :/
How do I tell rails to take the subject as is?

I had a look at this as a follow up to my answer to the previous question. The problem lies with TMail. It automatically removes and carriage returns from the subject. I created the following monkey patch as it seems to be the only solution to stop TMail's behaviour.
module TMail
class SubjectHeaderField < UnstructuredHeader
def parse
#Do nothing
end
end
class HeaderField
FNAME_TO_CLASS = FNAME_TO_CLASS.merge('subject' => SubjectHeaderField)
end
end
If you include it in the mailer in Rails 2.3.x it should work. Alternatively you might want to look at http://github.com/mikel/mail/ which is the default mailer in Rails 3?
Then you can set the header before encoding as the previous answer showed.

"This is a very very long subject line of an email that hotmail has problems processing".scan(/.{1,16}/)
#=> ["This is a very v", "ery long subject", " line of an emai", "l that hotmail h", "as problems proc", "essing"]
I have done it at 16 chars, here's a link to the doc http://www.ruby-doc.org/core/classes/String.html#M000812
HTH

Related

Rails friendly_id with arabic slug

My question is closely related to this one Rails friendly id with non-Latin characters. Following the suggested answer there, I implemented a little bit different solution (I know, it's primitive, but I just want to make sure it works before adding complex behavior).
In my user model I have:
extend FriendlyId
friendly_id :slug_candidates, :use => [:slugged]
def slug_candidates
[
[:first_name, :last_name],
[:first_name, :last_name, :uid]
]
end
def should_generate_new_friendly_id?
first_name_changed? || last_name_changed? || uid_changed? || super
end
def normalize_friendly_id(value)
ERB::Util.url_encode(value.to_s.gsub("\s","-"))
end
now when I submit "مرحبا" as :first_name through the browser, slug value is set to "%D9%85%D8%B1%D8%AD%D8%A8%D8%A7-" in the database, which is what I expect (apart from the trailing "-").
However the url shown in the browser looks like this: http://localhost:3000/en/users/%25D9%2585%25D8%25B1%25D8%25AD%25D8%25A8%25D8%25A7- , which is not what I want. Does anyone know where these extra %25s are coming from and why?
Meanwhile, I came a bit further, so I put my solution here maybe it could be helpful for someone else.
The 25s in the url seem to be the result of url_encoding the '%' in my slug. I don't know where this happens, but I modified my normalize_friendly_id function, so that it doesn't affect me anymore. Here it is:
def normalize_friendly_id(value)
sep = '-'
#strip out tashkeel etc...
parameterized_string = value.to_s.gsub(/[\u0610-\u061A\u064B-\u065F\u06D6-\u06DC\u06DF-\u06E8\u06EA-\u06ED]/,''.freeze)
# Turn unwanted chars into the separator
parameterized_string.gsub!(/[^0-9A-Za-zÀ-ÖØ-öø-ÿ\u0620-\u064A\u0660-\u0669\u0671-\u06D3\u06F0-\u06F9\u0751-\u077F]+/,sep)
unless sep.nil? || sep.empty?
re_sep = Regexp.escape(sep)
# No more than one of the separator in a row.
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
# Remove leading/trailing separator.
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, ''.freeze)
end
parameterized_string.downcase
end
Some comments on that:
I took only Latin and Arabic alphabets into account
I decided that if I allowed arabic characters in the url, then there is no sense to keep the friendly_id behavior of converting e.g. "ü" to "ue", "ö" to "oe", etc. So I leave such characters in the url.
I tried also to keep characters which might not be used in Arabic, but in other languages which use the Arabic alphabet such as Farsi or Urdu. I speak Arabic only, so I did a guess of which characters might be regarded as regular in other languages. For example is "ڿ" a regular character in any language? I have no idea, but I guess it could well be.
again, since I speak arabic, I stripped the "Tashkil" out of the text. I would say, that texts without tashkil are in general easier to read than the ones with. However, I don't know if I should take care of some similar stuff in other languages. Any hints are much appreciated.
Last: adding another alphabet would be as easy as adding the appropriate sequences to the regex. One only needs to know which characters should be white-listed.
I appreciate any comments or improvement suggestions.

How to use embedded ruby in ActionMailer subject

I'm trying to use some embedded ruby in the subject line of an email coming from ActionMailer but keep getting different errors.
I couldn't find any documentation on the proper syntax. Any resources to fix this line of code?
mail(to: #user.email, subject: "Your Reservation Confirmation for" + #restaurant.name)
I've passed in all of the variables fine. I just need to see how I can combine text and these inputs.
Thanks
There are two common ways to it:
First:(regarding rep)
"...Confirmation for" + #restaurant.name.to_s
Second:
you can use string interpolation
"...Confirmation for #{#restaurant.name}"
I don't know whether this is intentional, but apparently #restaurant.name is returning a number (as you clarified, you're getting a TypeError: no implicit conversion of Fixnum into String). Calling #restaurant.name.to_s will solve that.
As G.B mentioned in another answer, string interpolation like "...Confirmation for #{#restaurant.name}" works too, since it calls #to_s for you automatically.
I'm putting the solution into an answer, since we found it while clarifying in the comments.

Capitalization API

Is there any good API or service that handles capitalization well? It should be able to handle input like "i need help fixing my iphone asap" with a desired output of "I Need Help Fixing My iPhone ASAP".
Edit: This is in conjunction with titleize. Titleize doesn't handle words like "iPhone" and acronyms. I'm currently getting user input like "ceo" and titleize returns "Ceo", when I'd like "CEO". I'd prefer not to write a list of special capitalizations, especially if there is a good alternative.
Another alternative would be a library of words and the correct capitalization.
Have a look at #titleize in ActiveSupport::Inflector
"man from the boondocks".titleize # => "Man From The Boondocks"
"x-men: the last stand".titleize # => "X Men: The Last Stand"
"TheManWithoutAPast".titleize # => "The Man Without A Past"
"raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
Cut and paste from http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-titleize

How to automatically link to objects in text submission in Rails

So, you're in Github filing an issue and you refer to issue #31. Then, while writing this issue, you note that #johnmetta has suggested some possible solutions that he's working on. Then you hit "Submit New Issue" and when you do, "#31" and "#johnmetta" are links, and #johnmetta has been notified, and issue #31 has a notification that it has been referenced.
I realize that there are more than one technologies at work here (Javascript goodies, etc), but what I'm looking for are some examples of how to do this type of thing in the Rails world. It's an interestingly difficult subject to search for.
What I've come up with conceptually is:
Have some identifier, such as # or # that is reserved
Upon submission, search for that identifier in the appropriate attribute
Upon finding it, search for the appropriate model with a field matching what follows
Once finding that, replace that text string with a link
Optionally, do whatever necessary to notify the referenced object
That said, it seems like it's super simple (explicitly coded, assumes friendly_id).
def prettify_user_links(str, source):
result = str
str.scan(/(#\S+)+/).each do |mtch|
# Strip off whatever identifier we're using
search_string = mtch[0].gsub('#','')
# Search for the matching model in the appropriate table
user = User.find(search_string)
if user
# If we find a matching model, create some link text and link it
link_txt = "<a href=>'#{user.url}'>#{mtch}</a>"
result.gsub!(search_string, link_txt)
# Notification. Not sure how/where, maybe with a message bus, or something more brute-force like
Comment.create :user_id => user.id, :body => "You have been mentioned in #{link_to comment.excerpt, comment} by #{link_to comment.owner, owner}"
return result
That would be my first cut, but I feel there have to be much more elegant solutions.
An additional aspect to this question: How would you grab a snippit of surrounding text. The brute force way would be to search n words before and m words after that string and grab all of that, then grab that sub-string from the results and do the search. Still, seems like there'd be a more elegant solution.
What you've described is the basic way; anything else is not terribly more elegant. It's helpful to see it as two parts: one is on receipt of the comment (when you should do notifications) and the other is on display of the comment, when you should do linkification.
This allows you to keep the original comment in its original form, which is helpful.
Perhaps put an after_create (so notifications aren't sent on every edit) on the comment model (assuming a comment model that includes a 'body' field):
[edit: added contextual info]
after_create :notify_mentions
def notify_mentions
body.scan %r{(.{0,40})#(\w+)(.{0,20})} do |match|
username = match[1]
context = [match.first, match.last]
Notification.send(match, context, self) if User.exists?(:login => username)
end
end
I use \w+ in place of \S+ because people often say things like:
Hey #JohnMetta, how are you doing?
and \S+ will capture the , which might be wrong. Pulling the # out of the capture group lets me ignore it during notification.
The context in the above match groups consists of the 40 characters before and 20 characters after the matched username for your snippet. Adjust to taste.
Then when displaying the message, you essentially create a helper something like what you had:
def linkify(body)
body.gsub %r{#\w+} do |match|
link_to match, :controller => :users, :action => :show, :id => match
end
end
#gsub is awesome like that, in that it takes a block and replaces with the contents.
It's not a lot more elegant than what you had, but it should give a pretty decent result.

Best way to encode URLs?

I am currently developing a CMS and want to encode special chars in the URL in a nice way.
I don't want to use Rack::Utils.escape.
Is there already a cool gem available?
Best regards
Look at the stringex gem here, it can be used even without rails, but contains some stuff to make it easier to use(with rails).
Ruby's CGI library should do what you need:
url_encoded_string = CGI::escape("'Stop!' said Fred")
# => "%27Stop%21%27+said+Fred"
See http://ruby-doc.org/core/classes/CGI.html
Well, I normally use a handy custom-made method called String.to_slug. I hope you find it useful.
Call this /lib/to_slug.rb and include it in one initializer, or include it only on the model that generates the urls.
String.class_eval do
#converts accented letters into ascii equivalents (eg. ñ becomes n)
def normalize
#this version is in the forums but didn't work for me
#chars.normalize(:kd).gsub!(/[^\x00-\x7F]/n,'').to_s
mb_chars.normalize(:d).gsub(/[^\x00-\x7F]/n,'').to_s
end
#returns an array of strings containing the words on a string
def words
gsub(/\W/, ' ').split
end
#convert into a nice url-ish string
def to_slug(separator='-')
strip.downcase.normalize.words.join(separator)
end
end

Resources