Rails 3 scoped finds giving ActiveRecord::ReadOnlyRecord - ruby-on-rails

I am upgrading a Rails application from 2.3.10 to 3.0.4 and am running into an issue with updating models in my controller. I have been "scoping" model finds in order to prevent users from updating objects that don't belong to them. It works as expected in 2.3, but I get an ActiveRecord::ReadOnlyRecord error with update_attributes in Rails 3.
What is the right way to do this in Rails 3?
Project controller:
def update
#project = current_user.projects.find(params[:id])
if #project.update_attributes(params[:project])
# saved
else
# not saved
end
end

It turns out it was related to using scopes to impersonate active record associations. I was able to fix it by adding .readonly(false) to my scopes.

One possible solution is create new file config/active_record_monkey_patch.rb and add following content in it.
module ReadOnlyFalse
def self.included(base)
base.class_eval do
def readonly?
false
end
end
end
end
ActiveRecord::Base.send(:include, ReadOnlyFalse)
above code work for all models readonly(false).

Related

undefined method `distance_of_time_in_words' for Object

I am using Rails 5.2.2 There are many empty(nil) fields in my DB and created a custom method to use distance_of_time_in_words in my model without errors.
def my_distance_of_time_in_words
if self.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(self.accounts.first.updated_at,Time.now).titleize
end
end
And i am passing my object from view using :
<%= #customer.my_distance_of_time_in_words %>
It was working well and i restarted my PC and it says :
undefined method `distance_of_time_in_words' for #<Customer:0x00007f43b98601d8>
This is odd because as i said it was working as i expected.But it's not working now.
The date helpers are not available in your model by default, so you need to explicitly include them.
class Customer < ApplicationRecord
include ActionView::Helpers::DateHelper
def my_distance_of_time_in_words
if self.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(self.accounts.first.updated_at,Time.now).titleize
end
end
end
However a better approach would be to use a helper method to accomplish what you need, this way you won't need to explicitly include ActionView::Helpers::DateHelper since it is already available to you there:
module CustomersHelper
def my_distance_of_time_in_words(customer)
if customer.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(customer.accounts.first.updated_at,Time.now).titleize
end
end
end

Rails 4.2 - Made a 'services' directory for PORO helpers but console/controllers etc don't see it

I have some logic that is going to manipulate data before starting a job queue. However, inside the controller and also in the rails console I cannot seem to access the classes. Example:
In app/services/hobo_service.rb I have
class HoboService
def initialize
#api = Hobos::Api.new
end
def run
hobo
end
private
attr_reader :api
def hobo
api.hobo
end
end
However, if in my relevent controller I put
...
def create
#name = HoboService.new.run
end
...
Raises an exception saying the object cannot be found.
It seems as if all in the app directory should be in the pipeline and available. What am I missing here? Haven't been on Rails since 3.2 until recently.
I'm not sure why a subdirectory of app would be ignored, but let's try the simple solution- what happens when you add this to the Application class in your application.rb?
config.autoload_paths += %W(#{config.root}/app/services)

Observer is not working when updating records in Rails 2.3

I am facing a problem when using Observers in Rails 2.3.11. When I am creating a new object the observer is called and I get "Testing create" in my log file whereas when I am updating the object my observer is not called. May I know where I am going wrong. My model name is Termsmilestone.
class TermsmilestoneObserver < ActiveRecord::Observer
def after_update(termsmilestone)
Rails.logger.info("Testing Update *****************")
end
def after_create(termsmilestone)
Rails.logger.info("Testing create")
end
end
Whilst I don't have a specific answer for you, we've used Observers in Rails 4 in this way:
--
#config/application.rb
...
config.active_record.observers = :your_observer
#app/models/your_observer.rb
class YourObserver < ActiveRecord::Observer
def after_save(your)
...
end
end
--
This works for us in Rails 4 - I'm not sure about Rails 3 - but this works for us

Rails 3 respond_with returning associations

I have a simple Rails 3.2 controller which is consumed as an API with JSON:
module Api
module V1
class ReportsController < Api::V1::ApplicationController
def index
respond_with Report.where(name: params[:name])
end
end
end
end
(there is no code is the parent controllers)
When I consume this method, I'm getting all reports back, as expected, but I'm also getting all the associations. I don't see why this is and want to stop it.
Why am I getting the associations?
As #rmagnum2002 eluded to, I found active_model_serializers bundled into the app, and someone had created a serializer for that model without me noticing it.

Legacy table with column named "class" in Rails

I've got a legacy table that my rails application shares with another application. It has a column called "class". The first time I reference any attribute in that model, I get an error. Subsequent references to attributes work. Is there a good workaround for this, or should I just go modify the other application that uses this table (ugh)?
>> Member::Ssg.find(:first)
=> #<Member::Ssg ssg_key: #<BigDecimal:10b169688,'0.253E3',4(8)>, org_id: 2, academic_year: 2006, class: true, next_due_date: "2011-06-01", submitted_date: "2006-02-13", notes: nil, owner_id: "1">
>> Member::Ssg.find(:first).notes
NoMethodError: undefined method `generated_methods' for true:TrueClass
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
from (irb):2
>> Member::Ssg.find(:first).notes
=> nil
SOLUTION:
I went with a combination of the Bellmyer solution and adding the code below to my model
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
NOTE: Please see the updated solution at the end of this answer. Leaving the original outdated solution for historic reasons.
This has come up often enough (legacy column names interfering with ruby/rails) that I might just make a plugin out of this. Here's how you can fix it right away, though. Create this file in your app:
# lib/bellmyer/create_alias.rb
module Bellmyer
module CreateAlias
def self.included(base)
base.extend CreateAliasMethods
end
module CreateAliasMethods
def create_alias old_name, new_name
define_method new_name.to_s do
self.read_attribute old_name.to_s
end
define_method new_name.to_s + "=" do |value|
self.write_attribute old_name.to_s, value
end
end
end
end
end
And now, in your model:
class Member < ActiveRecord::Base
include Bellmyer::CreateAlias
create_alias 'class', 'class_name'
end
The first parameter to create_alias is the old method name, and the second parameter is the new name you want to call it, that won't interfere with rails. It basically uses the read_attribute and write_attribute methods to interact with the column instead of the ruby methods that get defined by ActiveRecord. Just be sure to use the new name for the field everywhere, like so:
member.class_name = 'helper'
This works with ruby 1.8, but I haven't tested with ruby 1.9 yet. I hope this helps!
UPDATE: I've found a better solution that works in Rails 3, the safe_attributes gem. I've written a blog post explaining how to use it, with example code snippets, and a full sample app you can download from github and play around with. Here's the link:
Legacy Database Column Names in Rails 3
The following works in Rails 6.0.2.2
class ReasonCode < ApplicationRecord
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
def as_json(options={})
add_class = attributes.keys.include?('class')
if add_class
if options[:only]
add_class = Array(options[:only]).map(&:to_s).include?('class')
elsif Array(options[:except])
add_class = Array(options[:except]).map(&:to_s).exclude?('class')
end
end
options[:except] = Array(options[:except])
options[:except].push('class')
json = super(options)
json['class'] = attributes['class'] if add_class
json
end
end
Adapted from this answer https://www.ruby-forum.com/t/activerecord-column-with-reserved-name-class/125705/2. The as_json method was added because rendering the record as json gave a SystemStackError (stack level too deep). I followed the serialization code in the Rails repo to only render the class attribute if specified in the as_json options.

Resources