record_timestamp = false not working from model - ruby-on-rails-3

I want to track the last_login DateTime of my user, without changing the updated_at attribute.
So inside my Model attribut I put:
def login!(session)
session[:user_id] = id
User.record_timestamp = false
self.touch(:last_login_at)
User.record_timestamp = true
end
also tried, which is the same:
def login!(session)
session[:user_id] = id
self.last_login_at = Time.now
User.record_timestamps = false
self.save(:validate => false)
User.record_timestamps = true
end
But update_at column still is updated after each login.
It seems that User.record_timestamps = false doesn't have any effect when being called from the model directly. (I use to call this method from controller or rake tasks without any problem)
please don't tell me to use update_attribute :last_login_at, Time.now which in Rails 3.1 doesnt set the updated_at column: I'm using rails 3.0.9!
Any idea?
It's really more DRY for me to do this update from the model and not from any controller...
--------------------
[edit] Hummmmmm seems like a bug in rails: I have a nested Class SubUser < User.
When I replace User.record_timestamps = false by self.class.record_timestamps = false then it's working. It's quite strange because:
1) I'm calling #user.login! with a real class User (User.first.login!)
2) even if I were calling SubUser.first.login! the command User.record_timestamps should affect too SubUser class, right?

This is the way I did this before, please give a shot.
def login!(session)
session[:user_id] = id
class << self
def record_timestamps; false; end
end
self.last_login_at = Time.now
self.save(:validate => false)
class << self
remove_method :record_timestamps
end
end
Let me know if it helps you anyway.

I would try using update_attribute because it doesn't do validations so maybe it doesn't update the timestamps either. I'm not sure if it will work:
def login!(session)
update_attribute :last_login_at, Time.now
end

Related

Rails 3: accept all params except a specific value

I have a Rails 3.2.13 Application to maintenance.
Because of authorization rules i want to limit the find(params[:file_registry_id]) method to accept all parameters except 752. (Only user tehen should be able to get it.)
def show
if current_user.tehen?
#file_registry = FileRegistry.find(752)
else
#file_registry = FileRegistry.find(params[:file_registry_id])
end
#rubric = Rubric.find(params[:id])
#rubrics = expanded_rubrics #rubric.ancestors_with_self.collect(&:id)
set_favorites
render :action => 'index'
end
Is there a method available to filter an element (here id 752) from the params hash? Or what's the best way to go?
Simple solution:
def show
#file_registry = get_file_registry
#....
end
private
def get_file_registry
if current_user.tehen?
FileRegistry.find(752)
else
unless params[:file_registry_id] == FORBIDDEN_ID_FOR_GUEST
FileRegistry.find(params[:file_registry_id])
else
false
end
end
end
FORBIDDEN_ID_FOR_GUEST should be defined outside of the controller, for example inside of a initializer.
But I suggest to use a authorization library like CanCan (https://github.com/ryanb/cancan) where you can define permissions for every use case.

How to stop a helper method from applying to a specific controller?

I have a helper_method that allows links to escape from a subdomain. However it is impacting my videos_controller, as it essentially seems to negate the 'current_event' method when not in the events controlller.
I've tried several dozen different ways over the last 4 days to make it so I can still escape my links from the subdomain, but still allow the videos_controller to work.
I think the best way to achieve this is to exclude the videos_controller from the helper method, but I'm not sure how (or if it is actually the best way forward - I'm obviously a noob!) Any suggestions please?! Relevant code below:
module UrlHelper
def url_for(options = nil)
if request.subdomain.present? and request.subdomain.downcase != 'www' and !options.nil? and options.is_a?(Hash) and options.has_key? :only_path and options[:only_path]
options[:only_path] = false
end
super
end
end
Videos_controller
def new
if current_event?
#video = current_event.videos.new
else
#video = Video.new
end
end
def create
if current_event.present?
#video = current_event.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
else
#video = current_user.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
end
if #video.save
flash[:success] = "Video uploaded!"
redirect_to root_url(subdomain: => current_event.name)
else
flash[:error] = "#{#video.errors.messages}"
render :new
end
end
current_event method
def current_event
if request.subdomain.present?
#event = Event.find_by_name(request.subdomain)
end
end
Did you take a look at this post yet?
You might want to create a new function test that only does something like
module UrlHelper
def test
puts "Test is called"
end
end
If that works you know its not including that fails but it has to be the method.
Otherwise you know the module is not included and you can narrow down the search.

ActiveRecord: Is it possible to get the number of DB queries executed in addition to total time in the Rails log?

For every request, I get this in the logs:
Completed 200 OK in 854ms (Views: 1.0ms | ActiveRecord: 17.0ms)
Is it possible to get it to also include the number of queries?
Something like:
Completed 200 OK in 854ms (Views: 1.0ms | ActiveRecord: 17.0ms | Queries: 10)
Ideally, I'd like all the "cached" ones to show up in that count too. Ie, even if the "cache" is saving me from "N+1" queries from hitting the DB, I still want to know I have a problem.
I'm fine with monkeypatching / manually editing something, since I really want this just for my dev box.
(If this can be made civilizedly so I can have it in production, that's even better, but if not, I'm fine with just having a manually modified Rails in my own machine)
Thanks!
Daniel
I know the ThinkingSphinx gem does something quite like this, adding the time spent running Sphinx queries to the summary in the log. You can probably do something similar ( maybe by making your own gem, since I bet other people would appreciate this functionality) to make the number of queries appear.
I haven't really looked hard at how it works, but it looks like modifications to ActionController and LogSubscriber are responsible:
lib/thinking_sphinx/action_controller.rb:
module ThinkingSphinx
module ActionController
extend ActiveSupport::Concern
protected
attr_internal :query_runtime
def cleanup_view_runtime
log_subscriber = ThinkingSphinx::ActiveRecord::LogSubscriber
query_runtime_pre_render = log_subscriber.reset_runtime
runtime = super
query_runtime_post_render = log_subscriber.reset_runtime
self.query_runtime = query_runtime_pre_render + query_runtime_post_render
runtime - query_runtime_post_render
end
def append_info_to_payload(payload)
super
payload[:query_runtime] = query_runtime
end
module ClassMethods
def log_process_action(payload)
messages, query_runtime = super, payload[:query_runtime]
messages << ("Sphinx: %.1fms" % query_runtime.to_f) if query_runtime
messages
end
end
end
end
lib/thinking_sphinx/active_record/log_subscriber.rb:
require 'active_support/log_subscriber'
module ThinkingSphinx
module ActiveRecord
class LogSubscriber < ActiveSupport::LogSubscriber
def self.runtime=(value)
Thread.current['thinking_sphinx_query_runtime'] = value
end
def self.runtime
Thread.current['thinking_sphinx_query_runtime'] ||= 0
end
def self.reset_runtime
rt, self.runtime = runtime, 0
rt
end
def initialize
super
#odd_or_even = false
end
def query(event)
self.class.runtime += event.duration
return unless logger.debug?
identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
query = event.payload[:query]
query = color query, nil, true if odd?
debug " #{identifier} #{query}"
end
def message(event)
return unless logger.debug?
identifier = color 'Sphinx', GREEN, true
message = event.payload[:message]
message = color message, nil, true if odd?
debug " #{identifier} #{message}"
end
def odd?
#odd_or_even = !#odd_or_even
end
def logger
return #logger if defined? #logger
self.logger = ::ActiveRecord::Base.logger
end
def logger=(logger)
#logger = logger
end
attach_to :thinking_sphinx
end
end
end
I hope this helps.

Rails 3 - creating own model and working with an others

I have two tables for checking views (visits of the page) - views of pic (PhotoView) in gallery and photographers(PhotographerView).
Because these two models (and tables) are the same, I want to create a model for them - something like:
class Func < ActiveRecord::Base
def self.check_views(model_view, data)
last_view = model_viewView.where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
model_view+View.new(...).save
model_view.increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
model_view+View.new(...).save
model_view.increment_counter(:views, data.id)
end
end #comparing dates
end
end
and call this method like:
#photo = Photo.find(params[:id])
Func.check_views('Photo', #photo)
When I try use it with the way above, I'll get the error undefined method `check_views' for Func(Table doesn't exist):Class
Could you give me a help, how to make it work?
Thank you
You can use ActiveRecord::Concern and modules to move the common functionality into one place as follows:
module CheckViews
extend ActiveSupport::Concern
module ClassMethods
# all class methods go here, if you don't have any just leave it blank
end
def check_views(data)
last_view = where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
##views_class.new(...).save
increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
##views_class.new(...).save
increment_counter(:views, data.id)
end
end #comparing dates
end
end
class Photo < ActiveRecord::Base
include CheckViews
end
you can now do the following:
#photo = Photo.find(params[:id])
#photo.check_views
I'd be very tempted to do this as a module extending the classes which want the Views functionality. Something like the following ought to work; but it's entirely untested and entirely unlike anything I've ever done before so it may be completely buggy. Fair warning.
module CheckViews
def self.extended(host_class)
host_class.class_variable_set("##views_class", "#{host_class}View".constantize)
end
def check_views(data)
last_view = where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
##views_class.new(...).save
increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
##views_class.new(...).save
increment_counter(:views, data.id)
end
end #comparing dates
end
end
class Photo < ActiveRecord::Base
extend CheckViews
...
end
(extend adds all the instance methods of the target Module as class methods of the calling class; so Photo gains Photo.check_views(data), and self in that function is the class Photo.)

Rails 3 - Building forms from Serialized Data

I've been working on a rails project where I am needed to serialize permissions for user roles and store in the database. As far as that goes I'm all good. Now my problem comes when I want to modify the serialized data from a rails generated form.
I acted on instinct and tried with the expected behavior.
That would be to use something like this:
f.check_box :permissions_customer_club_events_read
But as no getters or setters exist for the serialized data, this doesn't work (obviously :p). Now I wonder how I would go about tackling this problem and the only thing that comes to mind is dynamically generating getter and setter methods from my serialized hash.
Example:
def permissions_customer_club_events_read=(val)
permissions[:customer][:club][:events][:read] = val
end
def permissions_customer_club_events_read
permissions[:customer][:club][:events][:read]
end
Anyone understand what I'm getting at?
Here is my Model:
class User::Affiliation::Role < ActiveRecord::Base
require 'yajl'
class YajlCoder
def dump data
Yajl.dump data
end
def load data
return unless data
Yajl.load data
end
end
serialize :permissions, YajlCoder.new
after_initialize :init
def init
## Sets base permission structure ##
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
end
end
I suggest you have a look at something like attr_bucket. Ostensibly, this can be used to solve some inheritance annoyances, but it will also solve your problem for you. Here is the essence.
It looks like you know what all your permissions are, but you want to serialize all of them into the same database field. But within your actual rails app, you want to treat all your permissions as if they were totally separate fields. This is exactly what a solution like attr_bucket will let you do. Let's take your example, you would do something like this:
class User::Affiliation::Role < ActiveRecord::Base
attr_bucket :permissions => [:permissions_customer_club_events_read, :permissions_customer_club_events_write, :permission_do_crazy_things]
after_initialize :init
def init
## Sets base permission structure ##
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
end
end
Now you will be able to use permissions_customer_club_events_read, permissions_customer_club_events_write, permission_do_crazy_things as if they were separate database fields (this includes using them in forms etc.), but when you actually save your objects all those fields would get 'bucketed' together and serialized into the :permissions field.
The only caveat is the serialization mechanism, I believe attr_bucket will serialize everything using YAML, whereas you were using JSON. If this doesn't matter then you're golden, otherwise you might need to patch attr_bucket to use json instead of YAML which should be pretty straight forward.
Sorry if I did not understand the question ;)
You could have a customdata module, included in your model, and use method_missing:
module CustomData
def self.included(base)
base.instance_eval do
after_save :save_data
end
def method_missing(method, *args, &block)
if method.to_s =~ /^data_/
data[method] ? data[method] : nil
else
super
end
end
def data
#data ||= begin
#get and return your data
end
end
private
def save_data
end
end
With this method, you would have to use f.check_box :data_permissions_customer_club_events_read
It's not really complete, but I hope you get the idea ;)
attr_bucket seems like a good solution too.
This worked out for me in the end, this is how I solved it.
serialize :permissions, YajlCoder.new
after_initialize :init
def init
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")['customer']
build_attributes_from self.permissions, :permissions
end
private
def build_attributes_from store, prefix, path=[]
store.each do |k,v|
if v.class == Hash
build_attributes_from v, prefix, ( path + [k] )
else
create_attr_accessors_from prefix, ( path + [k] )
end
end
end
def create_attr_accessors_from prefix, path=[]
method_name = prefix.to_s + "_" + path.join('_')
class << self
self
end.send :define_method, method_name do
self.permissions.dig(:path => path)
end
class << self
self
end.send :define_method, "#{method_name}=" do |value|
self.permissions.dig(:path => path, :value => value)
end
end
And some monkey patching for hashes...
class Hash
def dig(args={})
path = args[:path].to_enum || []
value = args[:value] || nil
if value == nil
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
else
path.inject(self) do |location, key|
location[key] = ( location[key].class == Hash ) ? location[key] : value
end
end
end
end
Now getter and setter methods are generated for all of the serialized fields.