Hide a field in Ruby on Rails - ruby-on-rails

I've a field in my database called IP where I put the user IP (in #create method) when he send a message in my blog built in Rails.
But the field is visible when I want to see the articles in another format (JSON).
How can I hide the field IP?

You can do it in a format block in your controller like this:
respond_to do |format|
format.json { render :json => #user, :except=> [:ip] } # or without format block: #user.to_json(:except => :ip)
end
If you want to generally exclude specific fields, just overwrite the to_json method in your user model:
class User < ActiveRecord::Base
def to_json(options={})
options[:except] ||= [:ip]
super(options)
end
end
Update: In Rails 6, the method became as_json:
class User < ApplicationRecord
def as_json(options={})
options[:except] ||= [:ip]
super(options)
end
end

While this is not quite the right solution for passwords or for what is specifically asked, this is what comes up when you google for hiding columns in ActiveRecord, so I'm going to put this here.
Rails5 introduced new API, ignored_columns, that can make activerecord ignore that a column exists entirely. Which is what I actually wanted, and many others arriving here via Google probably do too.
I haven't tried it yet myself.
class User < ApplicationRecord
self.ignored_columns = %w(employee_email)
end
https://blog.bigbinary.com/2016/05/24/rails-5-adds-active-record-ignored-columns.html

Related

Associating a shipping address model with an order model Rails 4

I'm trying to associate a shipping address with an order in my application but cannot figure out a good way to do this.
This is the association I have:
class ShippingAddress < ActiveRecord::Base
belongs_to :order
end
and
class Order < ActiveRecord::Base
has_one :shipping_address
end
I see a lot of examples assuming there is a current_user, but I am trying to make the checkout process so that a user does not have to be signed in.
Here is what my Shipping Addresses controller looks like:
class ShippingAddressesController < ApplicationController
def new
#shipping_address = ShippingAddress.new
end
def create
#shipping_address = ShippingAddress.new(shipping_address_params)
#shipping_address.save
redirect_to root_url
end
private
def shipping_address_params
params.require(:shipping_address).permit(:shipping_name, :address_line1, :address_line2, :address_city,
:address_state, :address_zip, :address_country, :user_id, :order_id)
end
end
This is what the order controller looks like:
class OrdersController < ApplicationController
def create
#order = Order.find_or_initialize_by(item_id: params[:order][:item_id], order_completed: false, user_id: params[:order][:user_id])
#order.update_attribute(:quantity_requested, params[:order][:quantity_requested])
#order.save
redirect_to #order
end
private
def order_params
params.require(:order).permit(:user_id, :item_id, :quantity_requested, :quantity,
:order_completed, :sold, :shipping_address_id)
end
end
Can someone please let me know if I'm thinking about this the right way and how I can make the order aware of the shipping address?
Thanks in advance and sorry if I'm missing something obvious.
Not sure if you really need a :user_id in parameters. If user is logged in, you can get it via current_user. If not, you'll never know his id anyway.
Order is aware of the shipping address when you create it like order.create_shipping_address or ShippingAddress.create order: order (or populate shipping_addresses.order_id field any other way you like). And then you can use order.shipping_address and shipping_address.order to find related objects.
Also, usually managers(or site) have to communicate with customer anyway, at least send some emails regarding order status, so I don't require user to log in, but if he is not logged in, I create a new one on checkout to store his contacts(and for some other useful stuff :) )

Accessing current_user in a model in a Rails 3.2 app

I have a Rails 3.2 app. It is a publishing app where we kick off several Sidekiq jobs in response to changes in content. I was calling this from the controller but there's now getting to be multiple points of entry and are now duplicating logic in multiple controllers. The proper place for this to be is in a callback in the model. However, accessing current_user is frowned upon in the model but for things like logging changes or app events, it is critical.
So I have two questions (1) Is there something I'm missing regarding the argument about accessing current_user when you want to be logging changes across complex model structures? and (2) Is the proposed solution here an effective one with last update over 2 years ago in terms of thread-safety? I use a three Unicorn processes on Heroku. https://stackoverflow.com/a/2513456/152825
Edit 1
Thinking through this, wondering if I should just do something like this in my application.rb
class ArcCurrentUser
#current_user_id
def self.id
return #current_user_id
end
def self.id=id_val
#current_user_id=id_val
end
end
and then in my current_user method in application_controller, just update ArcCurrentUser.id to #current_user.id? I will only be using it for this logging functionality.
You're correct in that you can't access current_user from a model.
As for the answer you linked, I'm not entirely sure but I think it's not fully thread-safe. From the same question, I like this answer https://stackoverflow.com/a/12713768/4035338 more.
Say we have a controller with this action
...
def update
#my_object = MyModel.find(params[:id])
#my_object.current_user = current_user
#my_object.assign_attributes params[:my_model]
#my_object.save
end
...
and this model
class MyModel < ActiveRecord::Base
attr_accessor :current_user
before_save :log_who_did_it
private
def log_who_did_it
return unless current_user.present?
puts "It was #{current_user}!"
end
end
Or my favourite
...
def update
#my_object = MyModel.find(params[:id])
#my_object.update_and_log_user(params[:my_model], current_user)
end
...
class MyModel < ActiveRecord::Base
...
def update_and_log_user(params, user)
update_attributes(params)
puts "It was #{user}!" if user.present?
end
end

Rails 4: having a proxy model to combine multiple models

I am trying my hand on rails (4). I have done some Sinatra earlier.
I have a signup form, in which user can fill out his organization name, address and his own name, password etc. I have two tables - Users and Organizations, those table get populated with Signup data. So, I have two active records model users and organizations. My controllers looks as follows:
class SignupsController < ApplicationController
# GET /signup
def new
# show html form
end
# POST /signup
def create
#signup = Registration.register(signup_params)
respond_to do |format|
if #signup.save
format.html { redirect_to #signup, notice: 'Signup was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def signup_params
params[:signup]
end
end
I do not have any Registration model (table in db). What I am looking for such Registration model (should I call it model?) where I can have something like:
class Registration
def self.register params
o = Organization.new params
u = User.new o.id, params
self.send_welcome_email params[:email]
end
def send_welcome_email email
#send email here
end
end
1) So where should I keep this Registration class?
2) Is this a correct approach for such situation? If not, then what is the best way to do it?
3) Having such class will effect running any unit tests?
4) I see there is file, signup_helper.rb what is the use of that in SignupsController
You can do include ActiveModel::Model in your model, and it will behave as a normal Model. You will be able to do validations, callbacks. Anything other than persisting to a database.
Have a look at this for more info.
class Registration
include ActiveModel::Model
def self.register params
o = Organization.new params
u = User.new o.id, params
self.send_welcome_email params[:email]
end
def send_welcome_email email
#send email here
end
end
To answer your questions:
1) I would move the Registration class to a Signup module (as it relates to the signup use case) in app/models:
# apps/models/signup/registration.rb
module Signup
class Registration
...
end
end
2) I like your approach. It's something I would do. It is not the "Rails way" but in my opinion it is superior.
3) No. Having a PORO (plain old ruby object) is currently fine for any unit tests and you can easily test it.
4) Helpers are an (in my opinion) old and obsolete way to share functionality between views (and controllers). Don't use them unless there is absolutely no other way (maybe some Library demands it ... ). It is alway better to use POROs like you did.
You don't have to include ActiveModel::Model unless you need it's functionality. E.g. validations. You can include individual pieces of its functionality in this case. E.g
include ActiveModel::Validations

Devise with Associated Record validation

My background
I am/was a PHP developper. Have been for 15 years. Ruby is new to me (My new challenge)!
Current Setup
I am using Devise with a User model.
Rails: 3.2.1
Devise: 2.1.2
Use Case
When the user registers (going thru Devise controller), I want to create the User record but also a Foo record automatically. I created an after_create which handles the creation of the Foo record.
Class User < ActiveRecord::Base
after_create :make_foo
def make_foo
Foo.create(
:name => name,
:user_id => id
)
end
end
Class Foo < ActiveRecord::Base
belongs_to :user
end
Symptoms
I had a problem where when the Foo record was not being created (validation for example), then the User record was still created (I did not want that). I added a Raise Exception in after_create which rolls back the User creation.
However, I would prefer some nice error handling rather than Exception being throwed. Right now I get a 500 Error page with that Exception.
I would prefer that the form can be shown again with the reason(s) of the failure.
Class User < ActiveRecord::Base
after_create :make_foo
def make_foo
foo = Foo.create(
:name => name,
:user_id => id
)
if !foo.valid?
raise Exception.new('Foo creation failed.')
end
end
end
Plea for help
Any suggestions?
Instead of raising an exception you can redirect back to same page with setting flash message in
if !foo.valid?
block like this
flash[:error] = 'error msg'
and redirect using
session[:return_to] = request.referer
redirect_to session[:return_to]
I ended up overriding the Devise Resitrations Controller and putting a begin...rescue...end inside the create method.
# routes.rb
devise_for :users, :controllers => { :registrations => "my_devise/registrations" }
# app/controllers/my_devise/registrations_controller.rb
class MyDevise::RegistrationsController < Devise::RegistrationsController
def create
begin
super
rescue Exception
self.resource.errors[:base] << "My error message here"
clean_up_passwords resource
respond_with resource
end
end
end
You might want to look at Rails3: Devise User has_one relationship to see if better modelling can make the problem easier.
The way you are modelling user.rb now, is indeed such that a User may exist without Foo (which must belong to a User however), so it just calls :make_foo as an after_create callback with no other guarantees whatsoever.

How can I disallow updates except for on one field?

I've been preventing updates to certain models by using this in the model:
def update
self.errors.add_to_base( "Cannot update a #{ self.to_s }" )
end
I'm now writing a plugin that delivers some extra functionality to the model, and I need to update one field in the model. If I weren't using a plugin I would do this directly in the model...
def update
if self.changed == ['my_field']
super
else
self.errors.add_to_base( "Cannot update a #{ self.to_s }" )
end
end
I can't do the same from my plugin since I don't know if the update behaviour is the ActiveRecord default, or has been overridden to prevent updates. Is there another way to prevent record updates while allowing me to override for a specific field (and only in the instance where my plugin is applied to this model).
First, you should be using a before_update callback for that sort of thing rather than overriding update. Second, you can store the updatable attributes on the model, and then update them with the plugin. I just wrote this in the browser, so it could be wrong.
attr_accessor :updatable_attributes
before_update :prevent_update
private
def prevent_update
return true if self.changed == self.updatable_attributes
self.errors.add_to_base "Cannot update a #{ self.to_s }"
false
end
end
Late to the game here, but for people viewing this question, you can use attr_readonly to allow writing to a field on create, but not allowing updates.
See http://api.rubyonrails.org/classes/ActiveRecord/ReadonlyAttributes/ClassMethods.html
I think it has been available since Rails 2.0
The tricky part is, if you have any attributes that are attr_accessible you have to list your read only attributes there also (or you get a mass assignment error on create):
class Post < ActiveRecord::Base
attr_readonly :original_title
attr_accessible :latest_title, :original_title
end
Is this to prevent mass assignment? Would attr_accessible / attr_protected not do what you need?
Edit, just to illustrate the general point about the callback.
module MyModule
def MyModule.included(base)
base.send :alias_method_chain, :prevent_update, :exceptions
end
def prevent_update_with_exceptions
end
end
class MyModel < ActiveRecord::Base
before_validation :prevent_update
def prevent_update
end
include MyModule
end
I just use the rails params.require method to whitelist attributes that you want to allow.
def update
if #model.update(update_model_params)
render json: #model, status: :ok
else
render json: #model.errors, status: :unprocessable_entity
end
end
private
def update_prediction_params
params.require(:model).permit(:editable_attribute)
end

Resources