Rails 4 Validating email uniqueness without case_sensitive - mysql

Using Ruby 2.1.0, Rails 4.0.2, MySQL, and OS X, I can't get the validator uniqueness: { case_insensitive: XXX } to work right.
Yes, I realize this is almost, or perhaps precisely, identical to Rails 3. Validating email uniqueness and case sensitive fails. But the answer stream over there goes down a "don't do that" fork, and at least for pedagogic reasons I'd like to actually answer this question. Out of respect for the line taken over there, I've started a new question. Sorry if that's abusive.
My difficulty is that case_sensitive: true and case_sensitive: false produce the same results, even when (AFAICT) they oughtn't.
models/user.rb extract:
class User < ActiveRecord::Base
VALID_EMAIL_REGEX = /\A[\w+\-.]+#akamai\.com\z/i
validates :email, presence: true, uniqueness: { case_sensitive: false }, format: {
with: VALID_EMAIL_REGEX,
message: "Must be an Akamai email address."
}
end
spec/models/user_spec.rb extract:
require 'spec_helper'
describe User do
before { #user = User.new(email: "giuseppe#akamai.com") }
subject { #user }
it { should be_valid }
context "email" do
describe "is already taken (caserifically)" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
end
end
As written, the test passes (user_with_same_email is not valid). Cool.
However, if I change the validation's false to true, the test still passes (user_with_same_email is still invalid, presumably colliding). Uncool.
I know that MySQL/OSX is case-insensitive as regards table names (due to the case-insensitive underlying file system), but this doesn't extend to value comparisons, does it?
Ooo, just noticed the (unanswered) Ruby on Rails Tutorial - Michael Hartl - Test “User when email address is already taken”. I believe these are exact duplicates.

I don't know about your specific database setup, but yes, I would say that is most likely the issue.
This is a quote from the Rails API guide for :uniqueness:
Note that some databases are configured to perform case-insensitive searches anyway.
http://edgeguides.rubyonrails.org/active_record_validations.html#uniqueness
As far as I can tell, there isn't an official patch for this. Best I can find are these old discussions:
https://github.com/rails/rails/issues/1399
https://github.com/rails/rails/issues/613
Both mention various ways around it.
I'd say your best bet is running it against what your production environment will be like too. Seems it behaves differently on different platforms.

There is an issue with your VALID_EMAIL_REGEX, you must end it with /i to be case insensitive.

Related

Database Agnostic 'or' Query in Rails 4.2 Active Record

I have a scope on a Conversation model which maintains an encapsulation of messages between 2 users. I created a scope to help detect a 'Conversation' (to find or check exist) from two user objects.
I used to have:
validates_uniqueness_of :from, scope: :to
scope :between, -> (from,to) { where('("conversations"."from" = ? AND "conversations"."to" = ?) OR ("conversations"."from" = ? AND "conversations"."to" = ?)', from, to, to, from) }
However, while this worked for SQLite and Postgres; it wasn't working for MySQL. I need to convert this explicit query to either be completely database agnostic, or to be made out of active record relations instead.
I have had a shot at doing it myself using '.or' but I suspect this is only available from Rails 5+ because it gave me a undefined method error when I tried to use it.
Instead I have used:
validates_uniqueness_of :from, scope: :to
scope :between, -> (from,to) { where(from: [from, to], to: [to, from]) }
This code works great (and its actually really fast and succinct) but I am worried that it might be 'weak' - in that technically it could allow for a situation where from == from, to == from - which is supposed to be impossible for my application.
If a conversation is created between 'X' and 'Y', then from: X and to: Y = conversation_id: 3 and if Y messages X, then it should find conversation_id: 3 (since semantically they are the same).
Im happy enough to leave the new 'between' scope as is, but how can I strengthen the validation to ensure that from != to when the records are created (and then it doesn't matter that the query could potentially find conversations to and from the same user).
You could try rails_or, a gem which supports #or method in Rails 3, 4, 5.
Then write your scope as:
scope :between, -> (from,to) { where(from: from, to: to).or(from: to, to: from) }

ActiveModelSerializers Polymorphic Json

Been wrestling with trying to get polymorphic serializers working and testing data via rspec. Just upgraded to 0.10+
I found this post, which makes a lot of sense, and does give me a entry into generating the serializations, however, when doing it for polymorphs, I never get the type and id properly named (expecting to see asset_id and asset_type nested)
{:id=>1,
:label=>"Today I feel amazing!",
:position=>0,
:status=>"active",
:media_container_id=>1,
:asset=>
{:id=>4
Test ActiveModel::Serializer classes with Rspec
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
end
I noticed that the tests dont even seem to properly add the polymorphic identifiers either (ie asset_id, asset_type -- or in the test case imageable_id, imageable_type)
https://github.com/rails-api/active_model_serializers/commit/045fa9bc072a04f5a94d23f3d955e49bdaba74a1#diff-c3565d7d6d40da1b2bf75e13eb8e6afbR36
If I go straight up MediaSerialzer.new(media) I can poke at the .associations, but I cant seem to get them to render as if I was generating a full payload
From the docs
https://github.com/rails-api/active_model_serializers
serializer_options = {}
serializer = SomeSerializer.new(resource, serializer_options)
serializer.attributes
serializer.associations
Im pretty sure Im missing something/doing something wrong - any guidance would be great.
Thanks
It isn't easy to get the effect you are looking for, but it is possible.
You can access the hash generated by the serializer by overriding the associations method.
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
def associations details
data = super
data[:asset] = relabel_asset(data[:asset])
data
end
def relabel_asset asset
labelled_asset = {}
asset.keys.each do |k|
labelled_asset["asset_#{k}"] = asset[k];
end
labelled_asset
end
end
I learnt alot about ActiveModelSerializer to get the hang of this! I referred to Ryan Bates' podcast on the topic:
http://railscasts.com/episodes/409-active-model-serializers
In there he describes how you can override the attributes method and call super to get access to the hash generated by the serializer. I guessed I could do the same trick for the associations method mentioned in your post. From there it takes a little bit of Ruby to replace all the keys, but, if I have understood correctly what you require, it is technically possible.
Hope that helps!

return array in rails and storage it

I know that it could sound an "easy-question" but I am new in rails and I don't know if I am structuring correctly my app.
My app is "easy" it has to connect a webpage and collect all JPG links and store the links in a database.
In my "model folder" I have defined my functions:
class JPG < ActiveRecord::Base
acts_as_paranoid
validates :title, :link, presence: true
validates :link, uniqueness: true
attr_accessible :tag_list, :id, :title
#acts_as_taggable
def main_web
require 'rubygems'
require 'scrapi'
require 'uri'
Scraper::Base.parser :html_parser
scraper = Scraper.define do
array :items
process "div.mozaique>div", :items => Scraper.define {
process "div.thumb>a", :link => "#href"
}
result :items
end
uri = URI.parse(URI.encode(web))
return scraper.scrape(uri)
end
end
As you can see the function main_web returns an array.
Well my question is "easy". If I want to call the function and to storage the information in the database, how do I have to do it?
I don't want to show the information in the screen I only want to storage the data in the database but from where do I have to do the call? view? or controller?
I suggest you read more about the controller-view model. You need to call and store in the database from the controller.
If main_web returns an array then wouldn't #jpg = #jpg.main_web mean that #jpg is now an array instead of the Active Record model? That would seem to be why it wouldn't save. You might want to add a field to your database that holds the array you generate from main_web. Then instead of returning it you could just call save on your #jpg model.
Also a nice tip would be if you are having issues with models or other things really try typing 'rails console' on command line. You could run your 3 lines of code there to see if they worked. This wouldn't solve your issue but might allow you to play around with some things.
#jpg = JPG.new
#jpg = #jpg.main_web
#jpg.save
Replacing my initial answer after giving it more consideration...
You are doing this:
return scraper.scrape(uri)
which you assign to the #jpg instance. Thus, #jpg is no longer an instance of JPG and cannot be saved as such.
Also, it is not necessary to call and store from the controller. The controller is more a decision maker than anything. It determines state and routes control and information to the desired view or model. The model is where data should be managed. Though, that may be a matter of debate and is my opinion.
Recommended reading:
http://sirupsen.com/what-I-wish-a-ruby-programmer-had-told-me-one-year-ago/

rails alternative to enum and view integration

rails doesn't offer ENUM types, but I do need a data member which can accept only five values. Moreover, I want it to be integrated automatically with the Rails Forms Helper: select_tag.
What's the right solution to my situation?
P.S, I'd rather not to use external plugins, if built-in and neat solution exist.
I keep functionality like this as close to where it's used as possible.
If the values are used by a single model, just keep them in the model, e.g., if users have certain possible types, and only those types, it might look something like:
class User < ActiveRecord::Base
TYPES = %w{guest, paid, admin}
# Plus validation on the `type` field.
# Maybe plus a setter override that also validates.
end
When you need to refer to those types elsewhere, like as allowable values in a select:
User::TYPES
There are a number of valuable tweaks around this, like providing decorators to make them "human readable" (capitalized, spaced, whatever) or metaprogramming methods to allow things like:
user.is_guest? # Or...
user.make_guest! # Or...
user.guest!
I use my own small gem for this functionality because it's often the case that a full-blown association is just too much and provides no value. It allows things like:
class User < ActiveRecord::Base
simple_enum :user_type, %w{guest, paid, admin}
end
Using the tip from this blog post, which offers a very simple approach.
You can set in on your model and then use it on your controller or views.
In this case it will map the status with integers.
STATUS = { pending: 0, active: 1, inactive: 2, deleted: 3 }
def status
STATUS.key(read_attribute(:status))
end
def status=(s)
write_attribute(:status, STATUS[s])
end
Rails 4.1 has enums. I just upgraded to the beta and it's working like a charm!
http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
I tried active_enum gem, which is great, but it's not compatible with rails 4. The solution from Paulo works pretty well and you can extract the enum into a concern if you want, but it just started getting too heavy for me so I rather upgraded!
You can easily define the Enum as a helper in ApplicationHelper
class ApplicationHelper
def select_range
%w{"a", "b", "c", "d", "e"}
end
end
Then in view you can call select_range freely.

can't update the attribute with ActiveRecord

I want to swap the content in answers table with ActiveRecord.
code 1:
Archieve::Answer.find_each do |answer|
str = answer.content
dosomething() #change the value
answer.update_attribute(:content,str)
end
But It doesn't change the value of content.
code 2:
Archieve::Answer.find_each do |answer|
str = answer.content
dosomething() #change the value
answer.reload
answer.update_attributes(
:content => str
)
end
Before update the :content attributes, I reload the record every time.
It can indeed change the the value.
Why?
What's the difference between code 1 & code 2?
Source Code
###1 Post Debug Message:
Updated Post:
Changed?: false
valid?: true
errors: #<ActiveModel::Errors:0xa687568>
errors: #<ActiveModel::Errors:0xa687568 #base=#<Archieve::Answer id: 9997190932758339, user_id: 4163690810052834, question_id: 3393286738785869, content: "狗狗生病,好可怜呀,", is_correct: false, votes_count: 0, comments_count: 0, created_at: "2011-11-06 18:38:53", updated_at: "2011-11-06 18:38:53">, #messages={}>
possible ActiveRecord 3.1.1 bug
The OP mentioned to me that he uses require "active_record" in a stand alone script (not using rails runner).
There is no separate Rails application for his task, he just uses a script. This is not necessarily bad, and has worked in earlier ActiveRecord versions, e.g. 2.x AFAIK -- maybe this is a regression in Rails 3.1 due to a new dependency?
# the OP's require statements:
require 'rubygems'
require 'logger'
require 'yaml'
require 'uuidtools'
require 'active_record'
complete code here: https://raw.github.com/Zhengquan/Swap_Chars/master/lib/orm.rb
maybe a dependency is missing, or problem with AR 3.1.1 when initialized stand alone?
It could be a bug actually
It could be that update_attribute() triggers a bug in the dirty-tracking of attributes, which then incorrectly assumes that the object has not changed, and as a result it will not be persisted, although the implementation of update_attribute() calls save() (see code fragment below).
I've seen something like this with an older version of Mongoid -- could be that there is a similar hidden bug in your ActiveRecord version for update_attribute()
In the Rails Console monkey-patch update_attribute like this:
class ActiveRecord::Base
def update_attribute(name, value) # make sure you use the exact code of your Rails Version here
send(name.to_s + '=', value)
puts "Changed?: #{changed?}" # this produced false in the OP's scenario
puts "valid?: #{valid?}"
puts "errors: #{errors.inspect}"
save
end
end
then try to run your Code 1 again...
you shouldn't see "Changed?: false".. if it returns false, although you changed the attribute, then there is a bug in your ActiveRecord version and you should report it.
Code 1:
NOTE: check the definition of update_attribute() (singular) here:
(please read the fine-print regarding validations -- it doesn't sound like a good idea to use that method)
http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000400
See also:
Rails: update_attribute vs update_attributes
The source code for update_attribute() looks like this:
2260: def update_attribute(name, value)
2261: send(name.to_s + '=', value)
2262: save
2263: end
it could fail if there is a bug with the dirty-tracking of attributes...
Code 2:
The second code looks correct.
There are a couple of things to also consider:
1) which attributes did you define as accessible, via attr_accessible ?
e.g. only accessible attributes will be updated via update_attributes()
http://apidock.com/rails/ActiveRecord/Base/update_attributes
2) which validations do you use?
are you sure the validations pass for the record when you call update_attribute?
See also:
http://guides.rubyonrails.org/active_record_querying.html
http://m.onkey.org/active-record-query-interface
http://api.rubyonrails.org/classes/ActiveRecord/Base.html