can't update the attribute with ActiveRecord - mysql

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

Related

How do I get Rails 4.x streaming to work with MySQL when testing?

I created a new Rails 4.2.1 test project to try out the new streaming feature (the 'Live' one which I read about here). This project is set up to use MySQL for the database (I also tried Sqlite but couldn't repro the issue with it). The project is simple, consisting only of: 1) a model Test with 2 attributes (both strings). 2) a simple route resources :tests and 3) a simple controller tests_controller with one action index. The model and controller were generated by the standard rails generators, and only the controller was modified, as follows:
class TestsController < ApplicationController
include ActionController::Live
def index
response.headers['Content-Type'] = 'application/json'
response.stream.write('{"count": 5, "tests": [')
Test.find_each do |test|
response.stream.write(test.to_json)
response.stream.write(',')
end
response.stream.write(']}')
response.stream.close
end
end
When I run rails s and test by hand everything seems fine. But when I added a test (shown below) I get a strange error:
1) Error:
TestsControllerTest#test_index:
ActiveRecord::StatementInvalid: Mysql2::Error: This connection is in use by: #<Thread:0x007f862a4a7e48#/Users/xxx/.rvm/gems/ruby-2.2.2/gems/actionpack-4.2.1/lib/action_controller/metal/live.rb:269 sleep>: ROLLBACK
The test is:
require 'test_helper'
class TestsControllerTest < ActionController::TestCase
test "index" do
#request.headers['Accept'] = 'application/json'
get :index
assert_response :success
end
end
Note that the error is intermittent, coming up only about half the time. Also, even though testing by hand doesn't cause any errors I'm worried that when multiple clients hit the API at the same time that errors will occur. Any suggestions as to what's going on here would be much appreciated.
Pretty old, but you need to actually checkout a new database connection since ActionController::Live executes the action in a new thread:
The final caveat is that your actions are executed in a separate thread than the main thread. Make sure your actions are thread safe, and this shouldn't be a problem (don't share state across threads, etc).
https://github.com/rails/rails/blob/861b70e92f4a1fc0e465ffcf2ee62680519c8f6f/actionpack/lib/action_controller/metal/live.rb
You can even use an around_filter/around_action for this.

Rails 4 Validating email uniqueness without case_sensitive

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.

Rails : Benefit of URL path modification vs Namespace?

Many people talk about namespace as a way to change the URL that lead to their controller with a prefix (i.e. : /admin/movies instead of /movies)
Changing the URL path
The official doc explains that if we want to change the URL that lead to our controller with a prefix, we just have to change our resource in config/route.rb.
From this :
resources :movies
to this :
resources :movies, :path => "/admin/movies"
Implementing namespace
After a lot of googsearches, I'm wondering why so many people like to have namespace and what are the adavantages of namespace versus just modifying the URL path leading to a specific controller in the router file : myapp/config/route.rb
To implement a namespace, the official doc explains that you need rather the following modifications. You will see that this is a lot of work :
namespace :admin do
resources :movies
end
...and moves your movies_controller.rb controller to app/controllers/admin directory.
But if you follow these instructions, you'll get this error :
*"Expected /var/www/myapp/app/controllers/admin/movies_controller.rb to define Admin::MoviesController"*
*Then you realized that Rails expect this 'alien' writing at the begining of your movies_controller.rb : "Admin::"*
So you change the first line of your movies_controller.rb with :
class admin::MoviesController < ApplicationController
instead of :
class MoviesController < ApplicationController
But you again get another error :
"undefined local variable or method `admin' for main:Object"
Then you realized you forgot Ruby classes MUST be declared with a starting uppercase.
So you add a starting uppercase to your Ruby class :
class Admin::MoviesController < ApplicationController
But you still get an error :
*Missing template admin/movies/index, application/index with {:locale=>[:"fr-CH"], :formats=>[:html], :handlers=>[:erb, :builder, :rxls, :coffee, :haml]}. Searched in: * "/var/www/myapp/app/views"*
What the hell...? Oh did the doc forgot to say as well that controller's corresponding views must as well be moved to an admin directory within the app/view/ ?
So you create a directory and move your views to app/view/admin/
And you still get the same error.
Then you realized you forgot to include the movies folder itself within app/view/admin
So you did it.
You still got an error message :
*undefined method `movies_path' for #<#:0xa817c0c>*
This time you know that routing, controller and view work but you still have to change the pathes on all your views...
Moreover, if you are using the "respond_with" method in your controller file, you have to include the namespace as parameter. This is an example for the index operation :
def index
#movies = Movie.all
respond_with(:admin, #movies)
end
Moreover if you are using declarative_authentification gem (similar as cancan gem), you have to prefix the argument of your permited_to? method with the namespace. For example (with HAML syntax) :
- if permitted_to? :index, :admin_movies
// And prefix as well your path
= link_to 'Show all my movie", admin_movies_path
- if permitted_to? :show, :admin_movies
// And prefix as well your path
= link_to 'Show selected movie", admin_movie_path(#movie)
You realize you were going to create a namespace just for the url path conveniance, so you decide to give up, roll back your modif, and just add the following line in your route.rb file :
resources :movies, :path => "/admin/movies"
Which works immediately like a charm.
As testing purpose I created a new fake project "testapp" including namespace with the generator.
I performed a "rails generate scaffold admin/movie title:string" to check how does the generator handle namespace.
The resulting app/controller/admin/movies_controller.rb is rather different from what we expected it to be.
There are additionnal prefix "Admin::" in front of each instance variable declaration.
Here for instance the index method :
def index
#admin_movies = Admin::Movie.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #admin_movies }
end
end
I guess that this is because the generator as well moved the model movie.rb into app/models/admin directory
Moreover I see that there is another model in app/models : admin.rb
Which contains :
module Admin
def self.table_name_prefix
'admin_'
end
end
I have no idea how this method will be used nor by who ?
I notice as well that my table will be named "admin_movies" in the /db/migrate/2012blabla_create_admin_movies.rb
class CreateAdminMovies < ActiveRecord::Migration
def change
create_table :admin_movies do |t|
t.timestamps
end
end
end
Well, it seems there will have a lot more work (and mess-up) than I expected in order to put in place a namespace...
For what benefit/advantage ???
To use another url path ? This small modification in my controller
would be far more easier : resources :movies, :path =>
"/admin/movies"
To make it possible to use two different controllers using a same name ? Wouldn't it be easier to just put a prefix to my controler name than do all this mess-up ?
Of course some of the above errors could have been avoided by being more vigilant. But you know, we are just human, and when it's late in the evening while coding, maybe you wished this part of rails would have been, let's say, more human adapted...
I really don't understand why people get so excited to have namespace.
I woudld be gratefull, if an expert could explain what am I missing.
Many Thanks
If you have two different controllers with the same name but different functionality then you can use namespaced routes. You don't have to rename the controller for it. Check out "2.6 Controller Namespaces and Routing" at http://guides.rubyonrails.org/routing.html.
The idea is not to have a ton of controllers in one directory. Namespaces help you do that by grouping controllers in directories.
And as far doing all that work, it's a lot easier just to generate the controllers you need using the namespace paths, eg:
rake generate controller "Foo/Bars"
Then rails does all that heavy lifting so you don't have to.

Why did this RSpec text break after changing my Rails db from SQLite to MySQL?

The second example used to pass in SQLite and now fails under MySQL.
require 'spec_helper'
shared_examples_for "anything that caches its model" do
factory = self.described_class.table_name.singularize.to_sym
klass = self.described_class
it "maintains a cache key based on the current #{self.described_class.name.downcase} count" do
klass.cache_key.should == "#{self.described_class.table_name}-0-"
end
context "when there are #{self.described_class.table_name} in the database" do
it "includes the last updated #{self.described_class.name.downcase} in the cache key" do
e1 = Factory factory
e2 = Factory factory
klass.cache_key.should == "#{self.described_class.table_name}-#{self.described_class.count}-#{e2.cache_key}"
end
end
end
And the error I'm getting:
Failure/Error: klass.cache_key.should == "#{self.described_class.table_name}-{self.described_class.count}-#{e2.cache_key}"
expected: "deliveries-2-deliveries/2-20120315152232"
got: "deliveries-2-deliveries/1-20120315152232" (using ==)
I think the DRY benefits of doing the shared example this way is actually offset by the weird readability of this spec. I'm just trying to make a cache key that works for the entire class the way Rails instance cache keys work. If you can't answer my question but also know a better way to do this then I'd appreciate a recommendation there also.
Thanks!

seed_fu from existing database

I wonder if there's a way to auto generate seed_fu files from existing data ?
I'm using Rails 3 and MySql.
seed_fu plugin: https://github.com/mbleigh/seed-fu
Top answer no longer works:
SeedFu::Writer.write('path/to/file.rb',{ class_name: 'Person', constraints: [:first_name, :last_name]}) do |writer|
Person.all.each do |person|
writer << person.as_json
end
end
seed-fu writer uses <<(seed) method to introduce the seed data to the block, top answer will cause a missing block exception.
also you need to pass the class name and constraints inside {options} keys so the writer takes em as options parameters
EDIT
Updated the answer according to Rails Seed-Fu Writer why seed got commented out? #Albert Netymk comment.
It should be:
Person.all.each do |person|
instead of:
Person.each do |person|
You should use SeedFu::Writer to generate seed_fu files, see http://rubydoc.info/github/mbleigh/seed-fu/master/SeedFu/Writer for an example.