Ruby on Rails - Titleize Exceptions - ruby-on-rails

I have a model called Organizations that contains fields for its address. In the model I have the statement before_save { self.address_line_1 = address_line_1.titleize } and just realized this is changing addresses with PO Box to Po Box.
Another example: I also have a standard Users model with first name/last name. Titleize will change a person's first name from TJ to Tj. Or, if their last name is hyphenated it will go from Smith-Jones to Smith Jones.
With the PO box I would know the exception ahead of time, but not for user's names. Is there any way to allow for these exceptions at all while still having the core titlsize functionality?

I would recommend trying to avoid changing the semantics of titlelize, though, to avoid issues later when you might expect it, in another part of the same application, to do what it's really intended to do. Since you're looking for some fairly specialized functionality for titleize I'd create a new, similar method which you could monkey patch into the String class, as above, called something like, abook_titleize (address book titleize):
class String
def abook_titleize
if allow_titleize(self)
titleize
else
# Check for other behaviors, such as if "self" is all consonants
# or if self is found in a predetermined list of acronyms,
# perhaps return self.upcase
self.upcase
end
end
private
def allow_titleize(s)
# Write some code here that determines if you want this string
# to be titleized and return true if so, otherwise false
end
end
Or something like that. You could make this as simple or as elaborate as you wish. If you really want to change titleize bahavior itself (again, I wouldn't recommend), then:
class String
:alias_method :old_titleize, :titleize
def titleize
if allow_titleize(self)
old_titleize
else
...

Related

Is there a more idiomatic way to update an ActiveRecord attribute hash value?

Given a person ActiveRecord instance: person.phones #=> {home: '00123', office: '+1-45'}
Is there a more Ruby/Rails idiomatic way to do the following:
person_phones = person.phones
person_phones[:home] = person_phones[:home].sub('001', '+1')
person.update_column :phones, person_phones
The example data is irrelevant.
I only want to sub one specific hash key value and the new hash to be saved in the database. I was wondering if there was a way to do this just calling person.phones once, and not multiple times
Without changing much behaviour:
person.phones[:home].sub!('001', '+1')
person.save
There are a few important differences here:
You modify the string object by using sub! instead of sub. Meaning that all other variables/objects that hold a reference to the string will also change.
I'm using save instead of update_column. This means callbacks will not be skipped and all changes are saved instead of only the phones attribute.
From the comment I make out you're looking for a one liner, which isn't mutch different from the above:
person.tap { |person| person.phones[:home].sub!('001', '+1') }.save
You can use the before_validation callback on your model.
Like this:
class Phone < ApplicationRecord
validates :home, US_PHONE_REGEX
before_validation :normalize_number
private
def normalize_number
home.gsub!(/^001/, '+1')
end
end
Note: I haven't tested this code, it's meant to show an approach only.
If you're looking to normalize also an international number, evaluate if the use of a lib like phony wouldn't make more sense, or the rails lib https://github.com/joost/phony_rails based on it.
EDIT
since the comment clarify you only want to change the values of the hash in one like you can use Ruby's method transform_values!:
phones.transform_values!{|v| v.gsub(/^001/, '+1')}

Ruby error - Undefined Method

I am try to write a function that will find the items in an array which match the string passed to the function. See code below.
class Island
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}</i>
How i keep getting "undefined method 'filer' for #
I'm not sure what i'm doing wrong.
First let me object against the solution posted by #Sravan :
While it is true - and sometimes even a good solution - to monkey-patch a class, you have to be careful how to do it, because it may become a time bomb:
Ruby evolves, and new versions often add methods to existing classes. This means that if you add a method Array#search, and a new version of Ruby will also add a method of the same name, your new method will SILENTLY override the one in Ruby. You likely won't notice it for long time, until you are using a feature which is supposed to use Rubys Array#search - maybe by using something new in stdlib - and you get weird results. To track down this type of error can be a nightmare. This is exactly the case when you use search as a method name.
Now, how to do it then? Three possibilities:
(1) If you monkey-patch, use at least a method name which is unlikely to become part of the official interface. It might have your project's name as a prefix, or plenty of underscore characters, and so on. Note that this is not 100% foolproof: A later version of Ruby might add under the hood a private method with exactly the same name than the one you were choosing, but of course the odder your name, the less likely this will happen.
(2) If you don't like this idea of using "clumsy" names, you could at least test before defining the new method, whether it already exists, and throw an exception if it doesn't:
class Array
if self.method_defined?(:search)
raise "#{self.class}::search already defined"
else
def search(...)
...
end
end
end
(3) The third possibility is to avoid monkey-patching and keep the method in your Island class. In this case, the method definition would be different:
class Island
def self.filter(array, string)
...
end
end
and it would be called by
Island.filter(myarray, mystring)
UPDATE: Forgot a forth possibility:
(4) You can make Island a subclass of Array. I don't know what else you want to do with your islands, but maybe this is an option worth considering:
class Island < Array
def filter(string)
...
end
end
Of course, when invoking filter, you need to turn your array into an island, before you can use it:
list = Island.new([....])
Following ruby's convention over configuration, you can add/overwrite any method in any class
So, adding a function to array class makes it accessible to all the arrays. So, in this solution.
1) First thing is you have taken the filter function in Island class, instead, you need to take inside Array class since the list is an array.
class Array
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}
O/P:
anthony
andre
antoinette
2) Since Filter is a keyword as suggested by other answer, take another name for it. Eg: search
class Array
def search(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list.search("an"){|i| puts i}

Is this user method parameter well named?

I have a method to check if a User can edit either a post or a comment on my Rails application. Because a user can own both types of entities, I decided to make this method take either of them as a parameter post_or_comment:
class User < ActiveRecord::Base
def can_edit?(post_or_comment)
post_or_comment.user == self || self.admin?
end
end
Is this a good practice to ambiguously take any object like this as a parameter, and does the name I chose for the parameter make sense?
I am not interested in a sophisticated user-role handler like CanCan, as I am learning and would rather keep it simple.
If it's understood that within your schema a post is a type of comment or vice-versa then it's not really all that confusing to express it in the form of one or the other with the implication that it applies equally to both types.
Generally it's best to avoid overly restricting things unless you have a very good reason. There's ways of turning your very specific method into one that probably works most of the time, and if not it's because you're passing it an unowned thing:
def can_edit?(thing)
# Admin can edit anything.
return true if (admin?)
case (thing)
when User
# Users can edit themselves
thing === self
else
if (thing.respond_to?(:user))
# If the owner matches.
thing.user === self
else
# Don't really know, so say no by default.
false
end
end
end
The worst case failure state for this code is that it says "no". Now you can pass in arbitrary things that may or may not have a user property and it will work as expected. For other special cases you can add another when to the main case.

set constant values for cuisine like Chinese,Indian in ruby on rails

I want to use Cuisines like (Chinese, Indian, US) as constant values in my application which are defined in a config file. How can I set as constants and how can access in controllers?
This is explicitly not an answer to your question, but a suggestion that you look for alternatives. I think you would be far better off creating a database table with your cuisine names in it than to use constants. Leverage rails associations so that you can write nice readable code.
The problem with using constants is that under many circumstances, they aren't really constant. What happens if you want to add Japanese? What happens if you want to add Thai, but then 6 months later decide to drop it? What happens if you decide that Indian is too broad, and you want "Northern Indian" and "Southern Indian"?
With a database table, you can ensure that the class that are associated with those constants are always in a consistent state. When you need to get them all, they are just a line of code away with
my_cuisines = Cuisine.all
with nice built in iterators.
You can use gem 'settingslogic'
model settings.rb:
class Settings < Settingslogic
source "#{Rails.root}/config/settings.yml"
namespace Rails.env
end
then, use in controller:
Settings.cousines
First, consider what Marc Talbot said. Make sure that you really don't want a normal database model. If you're sure you want to use constants then continue on:
My preferred way to do this is with a pseudo-model.
In app/models/cuisine.rb
class Cuisine
# Should come before the constant declarations
def initialize(name)
#name = name
end
Mexican = new('Mexican')
Chinese = new('Chinese')
Indian = new('Indian')
def to_s
name
end
# other related methods
# like translations, descriptions, etc.
end
Then in the everywhere else in the app you can just reference Cuisine::Mexican or Cuisine::Indian
Also depending on how you are using it you might need a list of the cuisines.
class Cuisine
...
def self.all
[Mexican, Indian, Chinese, ...]
end
end
This technique keeps the code organized and keeps you from writing yet another initializer file.

Rename ActiveResource properties

I am consuming JSON data from a third party API, doing a little bit of processing on that data and then sending the models to the client as JSON. The keys for the incoming data are not named very well. Some of them are acronyms, some just seem to be random characters. For example:
{
aikd: "some value"
lrdf: 1 // I guess this is the ID
}
I am creating a rails ActiveResource model to wrap this resource, but would not like to access these properties through model.lrdf as its not obvious what lrdf really is! Instead, I would like some way to alias these properties to another property that is named better. Something so that I can say model.id = 1 and have that automatically set lrdf to 1 or puts model.id and have that automatically return 1. Also, when I call model.to_json to send the model to the client, I dont want my javascript to have to understand these odd naming conventions.
I tried
alias id lrdf
but that gave me an error saying method lrdf did not exist.
The other option is to just wrap the properties:
def id
lrdf
end
This works, but when I call model.to_json, I see lrdf as the keys again.
Has anyone done anything like this before? What do you recommend?
Have you tried with some before_save magic? Maybe you could define attr_accessible :ldrf, and then, in your before_save filter, assign ldrf to your id field. Haven't tried it, but I think it should works.
attr_accessible :ldrf
before_save :map_attributes
protected
def map_attributes
{:ldrf=>:id}.each do |key, value|
self.send("#{value}=", self.send(key))
end
end
Let me know!
You could try creating a formatter module based on ActiveResource::Formats::JsonFormat and override decode(). If you had to update the data, you'd have to override encode() also. Look at your local gems/activeresource-N.N.N/lib/active_resource/formats/json_format.rb to see what the original json formatter does.
If your model's name is Model and your formatter is CleanupFormatter, just do Model.format = CleanupFormatter.
module CleanupFormatter
include ::ActiveResource::Formats::JsonFormat
extend self
# Set a constant for the mapping.
# I'm pretty sure these should be strings. If not, try symbols.
MAP = [['lrdf', 'id']]
def decode(json)
orig_hash = super
new_hash = {}
MAP.each {|old_name, new_name| new_hash[new_name] = orig_hash.delete(old_name) }
# Comment the next line if you don't want to carry over fields missing from MAP
new_hash.merge!(orig_hash)
new_hash
end
end
This doesn't involve aliasing as you asked, but I think it helps to isolate the gibberish names from your model, which would never have to know those original names existed. And "to_json" will display the readable names.

Resources