I am trying to write a method in my "team" model but current_user is showing this error
undefined local variable or method `current_user' for #
def set_default_url
if current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
The method current_user is working fine for other models and controllers . I am calling this method like this .
has_attached_file :logo, :styles => {
:medium => "200x200#",
:thumb => "100x100#",
:small => "50x50#"
},
:default_url => :set_default_url
I am using rails 3.2 , ruby 1.9.3 and devise 3.1 . It seems to be simple task but I don't understand where the fault lies . I will be really thankful if someone helps me out here .
current_user is not available in any model, to access current_user in model do this
In your application controller
before_filter :set_current_user
def set_current_user
Team.current_user = current_user
end
in your Team model add this line
cattr_accessor :current_user
Congrats, now current_user is available in every model, to get the current user just use the following line everywhere
Team.current_user
NOTE: Restart the server after adding the lines mentioned above!
Now in your question you can use it like
def set_default_url
if Team.current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
Hope this helps!
If you are using it only once than while calling this method pass current_user as argument like
has_attached_file :logo, :styles => {
:medium => "200x200#",
:thumb => "100x100#",
:small => "50x50#"
},
:default_url => :set_default_url(current_user)
and in model
def set_default_url(current_user)
if current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
If you dont want above step then follow these
Go to User Model
def self.current_user
Thread.current[:user]
end
def self.current_user=(user)
Thread.current[:user] = user
end
Then go to application controller
before_filter :set_current_user
def set_current_user
User.current_user = current_user
end
Now we can easily fetch current_user in any model not only in Team
just give as User.current_user so in your code
def set_default_url
if User.current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
So make use of it.
Hope it solves your issue in a good way. Free to use in any model
User.current_user to fetch the current user
User.current_user= to assign the current user.
Thanks
Related
Facing very wired issue while using Paperclip (With s3 storage) and Apartment gem.
Apartment gem is being used for multi-tenancy with Postgres DB. ( i.e Separate Schema for every tenant )
For Storage, I would like to create a separate folder for every tenant.
class Media < ApplicationRecord
belongs_to :user
has_attached_file :file,
:storage => :s3,
:s3_credentials => {
:access_key_id => "ACCESSKEY",
:secret_access_key => "SECRETE KEY"
},
:path => "#{Apartment::Tenant.current}/:class/:attachment/:id_partition/:style/:filename"
end
Above is my media folder, which belongs to User.
class User < ApplicationRecord
has_attached_file :avatar,
:storage => :s3,
:s3_credentials => {
:access_key_id => "ACCESSKEY",
:secret_access_key => "SECRETE KEY"
},
:path => "#{Apartment::Tenant.current}/:class/:attachment/:id_partition/:style/:filename"
end
Apartment.rb file looks like below
require 'apartment/elevators/subdomain'
require 'rescued_apartment_middleware'
Apartment.configure do |config|
config.excluded_models = %w{ Account }
config.tenant_names = lambda { Account.pluck :subdomain }
config.use_schemas = true
end
Rails.application.config.middleware.insert_before Warden::Manager, RescuedApartmentElevator
Apartment::Elevators::Subdomain.excluded_subdomains = ['www','admin']
rescued_apartment_elevator.rb file looks like below
class RescuedApartmentElevator < Apartment::Elevators::Subdomain
def call(env)
begin
super
rescue Apartment::TenantNotFound
env[:apartment_tenant_not_found] = true # to be later referenced in your ApplicationController
#app.call(env) # the middleware call method should return this, but it was probably short-circuited by the raise
end
end
end
In application_controller.rb I have handled the code something like below
class ApplicationController < ActionController::Base
before_action :load_schema, :authenticate_user!, :set_mailer_host
before_action :configure_permitted_parameters, if: :devise_controller?
private
def load_schema
Apartment::Tenant.switch!
return if request.subdomain == "www"
if request.subdomain == ""
redirect_to root_url(subdomain: 'www')
else
if current_account
Apartment::Tenant.switch!(current_account.subdomain)
else
if request.env[:apartment_tenant_not_found]
redirect_to root_url(subdomain: 'www'), notice: 'Account you are looking for do not exists'
end
end
end
end
end
I start my server, go to one of the tenant e.g http://apple.lvh.me:3000 (apple is tenant name here)
1. If I go to edit user profile and upload the file for user profile,
Apartment::Tenant.current is returning "public" in model and hence
the file is getting uploaded in public folder instead of "apple"
folder.
I am using devise gem (latest version) below is how my user_controller.rb looks like
class UsersController < ApplicationController
before_action : set_user, only: [:edit,:update]
def update
if #user.update(user_params)
if #user.pending_reconfirmation?
redirect_to edit_user_path(current_user), notice: "Your profile is successfully updated! But if you changed the email address, then please confirm it before!"
else
redirect_to edit_user_path(current_user), notice: "Your profile is successfully updated!"
end
else
render "edit"
end
end
end
Problem 2
**When I go to media and add some media, they would go properly to "apple" folder on s3 bucket. I would sign out, go to different tanent i.e http://google.lvh.me:3000/ (google is tenant name here). If I go to media listing, URL will continue to point to Apartment::Tenant.current -> apple only. This results in wrong URL for google tenant and images wont display.
If I shut down and restart the server, then rails will start returning google for Apartment::Tenant.current method and hence URL pointing correctly to correct folder. **
To Summerise, below are my problems.
Using Apartment::Tenant.current in model for paperclip custom path -
is it right way? Is there some other method that could return me
current tanent so I can use it for creating folder on s3 bucket.
Have I configured the middleware correctly in my apartment.rb file?
Is there different way of doing this?
After posting to StackOverflow, I was able to figureout the solution. Posting it here in case someone else stumbleupon this in future.
My Mistake was tryign to use #{Apartment::Tenant.current} into path directly. This is wrong. paperclip support Interpolates, I should be using that in order to generate dynamic paths.
so below are the changes required.
has_attached_file :file,
:storage => :s3,
:s3_credentials => {
:access_key_id => "AccessKey",
:secret_access_key => "SecreteKey"
},
:path => ":tenant/:class/:attachment/:id_partition/:style/:filename"
Notice the :tenant being introduced in the path.
Add following line into paperclip.rb
Paperclip.interpolates :tenant do |attachment, style|
Apartment::Tenant.current
end
This is it. Hope it helps someone!
I'm trying to think of a best solution for following scenario. I've a model called an 'Article' with an integer field called 'status'. I want to provide class level array of statuses as shown below,
class Article < ActiveRecord::Base
STATUSES = %w(in_draft published canceled)
validates :status, presence: true
validates_inclusion_of :status, :in => STATUSES
def status_name
STATUSES[status]
end
# Status Finders
def self.all_in_draft
where(:status => "in_draft")
end
def self.all_published
where(:status => "published")
end
def self.all_canceled
where(:status => "canceled")
end
# Status Accessors
def in_draft?
status == "in_draft"
end
def published?
status == "published"
end
def canceled?
status == "canceled"
end
end
So my question is if this is the best way to achieve without having a model to store statuses? And secondly how to use these methods in ArticlesController and corresponding views? I'm struggling to understand the use of these methods. To be specific, how to do following?
article = Article.new
article.status = ????
article.save!
or
<% if article.in_draft? %>
<% end %>
I greatly appreciate any sample code example. I'm using rails 4.0.0 (not 4.1.0 which has enum support).
You could define all the methods using define_method, and use a hash instead of an array:
STATUSES = {:in_draft => 1, :published => 2, :cancelled => 3}
# Use the values of the hash, to validate inclusion
validates_inclusion_of :status, :in => STATUSES.values
STATUSES.each do |method, val|
define_method("all_#{method)") do
where(:status => method.to_s)
end
define_method("#{method}?") do
self.status == val
end
end
In that way, you can add statuses in the future without needing to create the methods manually. Then you can do something like:
article = Article.new
article.status = Article::STATUSES[:published]
...
article.published? # => true
I'm having issue with a youtube video being destroyed properly in a nested belongs_to has_one relationship between a sermon and its sermon video when using :dependent => :destroy.
I'm using the youtube_it gem and have a fairly vanilla setup.
The relevant bits below:
the video controller --
def destroy
#sermon = Sermon.find(params[:sermon_id])
#sermon_video = #sermon.sermon_video
if SermonVideo.delete_video(#sermon_video)
flash[:notice] = "video successfully deleted"
else
flash[:error] = "video unsuccessfully deleted"
end
redirect_to dashboard_path
end
the video model --
belongs_to :sermon
def self.yt_session
#yt_session ||= YouTubeIt::Client.new(:username => YouTubeITConfig.username , :password => YouTubeITConfig.password , :dev_key => YouTubeITConfig.dev_key)
end
def self.delete_video(video)
yt_session.video_delete(video.yt_video_id)
video.destroy
rescue
video.destroy
end
the sermon model --
has_one :sermon_video, :dependent => :destroy
accepts_nested_attributes_for :sermon_video, :allow_destroy => true
In the above setup, all local data is removed successfully; however, the video on youtube is not.
I have tried to override the destroy action with a method in the model, but probably due a failing of my understanding, can only get either the video deleted from youtube, or the record deleted locally, never both at the same time (I posted the two variants below and their results).
This only serves to destroy the local record --
def self.destroy
#yt_session ||= YouTubeIt::Client.new(:username => YouTubeITConfig.username , :password => YouTubeITConfig.password , :dev_key => YouTubeITConfig.dev_key)
#yt_session.video_delete(self.yt_video_id)
#sermon_video.destory
end
This only serves to destroy the video on youtube, but not the local resource --
def self.destroy
#yt_session ||= YouTubeIt::Client.new(:username => YouTubeITConfig.username , :password => YouTubeITConfig.password , :dev_key => YouTubeITConfig.dev_key)
#yt_session.video_delete(self.yt_video_id)
end
Lastly, the link I'm using to destroy the sermon, in case it helps --
<%= link_to "Delete", [#sermon.church, #sermon], :method => :delete %>
Thanks for your help, very much appreciated!
It looks as though I have just solved the issue; however, I'll leave it open for a bit in case someone has a more elegant / appropriate solution.
In the sermon video model I added --
before_destroy :kill_everything
def kill_everything
#yt_session ||= YouTubeIt::Client.new(:username => YouTubeITConfig.username , :password => YouTubeITConfig.password , :dev_key => YouTubeITConfig.dev_key)
#yt_session.video_delete(self.yt_video_id)
end
And the key thing, I believe, to have added in the sermon model was this --
accepts_nested_attributes_for :sermon_video, :allow_destroy => true
def has_photo
if user_signed_in?
#user = User.where(:id => current_user.id).first
if #user.has_photo?
if Asset.where(:attachable_id => current_user.id).count < 4
def sub_layout
"application"
end
render :template => "profiles/no_photo"
end
end
end
end
What would be the correct way to compare the Asset.count ?
Asset.where is a query, you would be much better using relationships for this.
If
Class User
has_many :assets
end
Class Asset
belongs_to :user
end
You could just use:
#user.assets.count < 4
As long as Asset has a user_id field (or make the relationship use :attachable_id) that is correctly set (the relationship can do that too if you create the Asset correctly)
By the way, if :id is unique for each user (it should be) you can rewrite
#user = User.where(:id => current_user.id).first
as
#user = User.find(current_user.id)
Hope it helps
I'm trying to upload to a portfolio app I've built, specifically trying to find where to hook delayed_job into the process. It all works otherwise. Right now it returns undefined method 'call' for #<Class:0xae68750> on app/controllers/portfolio_items_controller.rb:18:in 'create' so here's my model and that portion of the controller... anyone see anything that could be going wrong? The hook I'm using now I got from this blog: http://madeofcode.com/posts/42-paperclip-s3-delayed-job-in-rails
/app/controllers/portfolio_items_controller.rb
def create
#portfolio_item = PortfolioItem.new(params[:portfolio_item])
if #portfolio_item.save
flash[:notice] = "Portfolio item created. As soon as files are uploaded Portfolio item will be made live."
redirect_to #portfolio_item
else
render :action => 'new'
end
end
/app/models/asset.rb
class Asset < ActiveRecord::Base
attr_accessible :image, :image_file_name, :image_content_type, :image_file_size, :portfolio_item_id, :order
belongs_to :portfolio_item
has_attached_file :image,
:styles => {
:thumb => "20x20#",
:small => "100x100",
:large => "600x600>"
},
:storage => :s3,
:s3_credentials => {
:access_key_id => ENV["S3_KEY"],
:secret_access_key => ENV["S3_SECRET"]
},
:bucket => ENV["S3_BUCKET"],
:path => "portfolio/:attachment/:id/:style/:basename.:extension"
before_source_post_process do |image|
if source_changed?
processing = true
false
end
end
after_save do |image|
if image.source_changed?
Delayed::Job.enqueue ImageJob.new(image.id)
end
end
def regenerate_styles!
self.source.reprocess!
self.processing = false
self.save(false)
end
def source_changed?
self.source_file_size_changed? ||
self.source_file_name_changed? ||
self.source_content_type_changed? ||
self.source_update_at_changed?
end
end
class ImageJob < Struct.new(:image_id)
def perform
Image.find(self.image_id).regenerate_styles!
end
end
Edit: thanks to kind people, it's not the missing .new anymore. But now it's that the before_source_post_process is not defined? And I can't find that method in anywhere but that blog post and this SO question. Is there something more appropriate?
The before_source_post_process won't work for you. It only works for:
has_attached_file :source
In your case it should be
before_image_post_process
Similarly, the source_changed? method should be:
def source_changed?
self.image_file_size_changed? ||
self.image_file_name_changed? ||
self.image_content_type_changed? ||
self.image_update_at_changed?
end
I think this:
#portfolio_item = PortfolioItem.(params[:portfolio_item])
should most likely be this:
#portfolio_item = PortfolioItem.new(params[:portfolio_item])