Paperclip provides the following code:
has_attached_file :image,
...
...
which is duplicated across several models.
Extracting to a module:
module AttachedImage
include Paperclip::Glue
has_attached_file :image,
...
...
raises Exception encountered: #<NoMethodError: undefined method 'class_attribute' for AttachedImage:Module> exception.
What does this mean? How to mix-in Paperclip has_attached_file code? Is there a better way to eliminate duplication?
I was looking for a similar solution just now and came up with the following:
module AttachedFileModule
extend ActiveSupport::Concern
included do
attr_accessible :avatar
extend Paperclip::Glue
has_attached_file :avatar,
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_url => "/images/:style/missing.png"
end
end
As you can see in the above code we have to extend ActiveSupport::Concern so we can ise the attr_accessible method. We must also extend Paperclip::Glue so we can use the has_attached_file method.
Then in the model you want to attach images to:
class User < ActiveRecord::Base
include AttachedFileModule
end
Related
I'm following this tutorial on how to nest other Models in my Devise registration form. I'm getting an error in my New controller:
'NoMethodError in Users::RegistrationsController#new undefined method `languages_user=' for #'.
Languages_Users is a join table, and I'm wondering if this is the reason it isn't working, but I don't understand what the solution is. I want to add 2 different records of Languages_Users when the user signs up.
Models:
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100#" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates_presence_of :first_name, :last_name, :location, :nationality, :bio
before_save :assign_role
def assign_role
self.role = Role.find_by name: "user" if self.role.nil?
end
has_many :languages_users
has_many :languages, :through => :languages_users
accepts_nested_attributes_for :languages_users
Language.rb
class Language < ActiveRecord::Base
has_many :languages_users
has_many :users, :through => :languages_users
end
Langauges_user.rb
class LanguagesUser < ActiveRecord::Base
belongs_to :user
belongs_to :language
validates_presence_of :user_id, :language_id, :level
validates :user_id, :uniqueness => {:scope => :language_id, :message => 'can only delcare each language once. Please change the level of the language in Manage Languages.'}
end
Controllers:
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def new
build_resource({})
self.resource.languages_user = LanguagesUser.new[sign_up_params]
respond_with self.resource
end
def create
#user_id = current_user.id
super
end
def sign_up_params
allow = [:email, :password, :password_confirmation, [languages_user_attributes: [:language_id, :user_id, :level]]]
end
end
Relevant sections of User's new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.fields_for :langauges_user do |lu| %>
<%#= lu.text_field :language_id %>
<%= lu.collection_select(:language_id, Language.order('language ASC').all, :id, :language) %><br>
<%= lu.hidden_field languages_user[level], value: 1 %>
<% end %>
<%= f.submit "Sign up" %>
<% end %>
Relevant routes
Rails.application.routes.draw do
resources :languages_users
devise_for :users, controllers: { registrations: "users/registrations" }
resources :users
get 'languages_users/:id/sign_up', to: 'languages_users#sign_up', as: 'sign_up'
end
I'm still learning - so please let me know if you need to see anything else. Thanks!
I'm not that up to speed on Devise as I only recently started using it myself, but if I understand correctly it's not a Devise related problem - just harder to get a fix on because of Devise's self.resource abstraction.
You've deviated from your tutorial in an important respect: in the tutorial a User creates a Company, but the Company has_many :users. In your case the User creates a LanguagesUser, but here, the User has_many :languages_users. This means new syntax. This line, that's causing your error currently:
self.resource.languages_user = LanguagesUser.new[sign_up_params]
Needs to be along the lines of:
self.resource.languages_users.build(sign_up_params) #but see below re sign_up_params
Or if you want to save the associated resource right off the bat (I assume not, since you're not at the moment), you can use create or create! instead of build.
Aside
You may run into different trouble with your sign_up_params method, which also appears to have deviated from the tutorial - it doesn't actually use the allow array to whitelist any params, at least as written in your question. In any case, note they didn't use it when instantiating the Company, so it may not be fit for purpose when building your LanguagesUser, either.
A simple call to sign_up_params[:languages_user_attributes] should get you over the line, once you've fixed the sign_up_params method. Or you can set the nested object up with its own params whitelist.
I'm building a web API with Ruby and Grape. I have two classes that requires each other which leads to a situation where I get uninitialized constant class errors. The place where I get the error is in the Entity class for Connector, see the example code below, which requires Card::Entity before it has been inintialized. Is there any way to solve this probelm without moving the Entity definitions to another file?
#card.rb
require_relative 'connector'
require_relative 'caption'
class Card < ActiveRecord::Base
belongs_to :medium
belongs_to :storyline
has_many :connectors, autosave: true
has_many :connected_cards, class_name: "Connector", foreign_key: "connected_card_id"
has_many :captions
accepts_nested_attributes_for :connectors, :captions
class Entity < Grape::Entity
expose :id, documentation: { readonly: true }
expose :cardtype
expose :connectors, using: Connector::Entity
expose :captions, using: Caption::Entity
end
end
#connector.rb
require_relative 'card'
class Connector < ActiveRecord::Base
has_one :card
has_one :connected_card, :class_name => "Card", :foreign_key => "connected_card_id"
class Entity < Grape::Entity
expose :id, documentation: { readonly: true }
expose :title
expose :card, using: Card::Entity
expose :connected_card, using: Card::Entity
end
end
I don't know a lot about grape, but this could be solved by "pre declaring" the class:
#card.rb
require_relative 'caption'
class Connector < ActiveRecord::Base
# empty declaration just to make the import works
end
class Card < ActiveRecord::Base
belongs_to :medium
belongs_to :storyline
has_many :connectors, autosave: true
has_many :connected_cards, class_name: "Connector", foreign_key: "connected_card_id"
has_many :captions
accepts_nested_attributes_for :connectors, :captions
...
end
Still, I think that QPaysTaxes may have a valid point about design here.
I would like to override a Lib Model in my Models and add a relation.
What is the best way to do it ?
Example of a model in rpush lib:
https://github.com/rpush/rpush/blob/f82cc6a25861612ce118b2661f5a47bceb7ebd86/lib/rpush/client/active_record/app.rb
module Rpush
module Client
module ActiveRecord
class App < ::ActiveRecord::Base
self.table_name = 'rpush_apps'
if Rpush.attr_accessible_available?
attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key, :client_id, :client_secret
end
has_many :notifications, class_name: 'Rpush::Client::ActiveRecord::Notification', dependent: :destroy
validates :name, presence: true, uniqueness: { scope: [:type, :environment] }
end
end
end
end
I would like to add a has_many relation without editing the gem
So I thought creating a models/app.rb with this would be a start:
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
I tried this but nothing changed. Maybe my models/app.rb is not called ?:
module Rpush
module Client
module ActiveRecord
module App
def self.included(includer)
includer.class_eval do
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
end
end
end
end
end
How should I do it ? Is there a way to extend a lib model without removing the original behavior ?
Thanks !
EDIT
I Made it work but only by putting this code directly in config/initializers/rpush.rb
It wasn't working in models/app.rb
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
If someone has a nicer idea, I'll take it !
Extend the class with class << self
class Rpush::Client::ActiveRecord::App
class << self
[your methods here]
end
end
My project:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :email,
:password,
:password_confirmation,
:first_name,
:last_name,
:birth_date,
:residence,
:user_role,
:show_email,
:avatar
as_attached_file :avatar,
:default_url => '/images/system/user_avatars/default/default_avatar.png',
:url => "/public/images/system/user_avatars/:id_:style.:extension",
:path => "/public/system/user_avatars/:id_:style.:extension"
def update_profile(user_id, params) #params has :category and :user params
#user = User.find(user_id)
#user.update_attributes(params[:user])
return params[:category]
end
end
So, from my controller i call this method and i get no error. Paperclip shows attachment saved. The database is updated, but the image file is not saved. I have an registration made from scratch, so that's why i have the "attr_accessor :password"
I checked:
Have :multipart => true in form
Have attr_accessible :avatar in user model
Can any one give me some lead, cos i cant figure, why paperclip dosnt save the file.
Set attr_accessible :avatar_file_name as well, and you also need a paperclip.rb initializer:
require "paperclip"
Paperclip.options[:command_path] = "/ImageMagick"
And, of course, have ImageMagick installed.
I want to implement before_validaton callback in a separate class so that it can be reused by multiple model classes.
Here in callback i want to strip field passed as parameter but i am not sure how to pass parameter to callback class. Also i want to pass this as reference rather than by value(not sure if this concept is in Ruby Rails). I am following the link http://guides.rubyonrails.org/active_record_validations_callbacks.html#callback-classes
Here is code which is not completely correct, please help for same
class StripFieldsCallback
def self.before_validation(field)
field = field.strip
end
end
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation StripFieldsCallback(name)
end
If i define method in model in itself rather than defining in separate callback class code is like this (which works fine)
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation :strip_blanks
protected
def strip_blanks
self.name = self.name.strip
end
end
Of course it is not good to replicate methods in all of models so i want to define method in callback classes.
You may do this or use normalize_attributes gem
module StripFieldsCallback
def before_validation_z(field)
write_attribute(field, read_attribute(field).strip) if read_attribute(field)
end
end
class User < ActiveRecord::Base
include StripFieldsCallback
before_validation lambda{|data| data.before_validation_z(:name)}
end