4

I have Picture model, which store pictures from whole app, in imageable column I store Image owner object. I want to have an ability to save this pictures with different resolution.

class Picture < AbstractModel
  belongs_to :imageable, polymorphic: true
  mount_uploader :image, ImageUploader
end

class User < AbstractModel
  has_one :picture, class_name: Picture, as: :imageable, dependent: :destroy
  accepts_nested_attributes_for :picture
end

class Event < AbstractModel
  has_one :picture, class_name: Picture, as: :imageable, dependent: :destroy
  accepts_nested_attributes_for :picture
end

In Carrierwave I've made

class ImageUploader < CarrierWave::Uploader::Base
  version :xs if: :for_user? do
    process resize_to_fit: [100, 100]
  end

  private

    def for_user?(_picture)
      # And here I hit the problem!
      # model.is_a? User
    end
end

How I can fetch the picture owner model here?

model variable references on Picture model, I've tried all vars with pry, but did not succeed.

Of course I can store image link directly in User and Event in this snippet, but, in realworld code I have about 15 different models using images and I have to provide an ability to edit it in ActiveAdmin CMS(but it is another story :)

UPDATE

The main problem happens, when I create new record. So, I'm tried to add inverse_of, but it did not helps.

class Picture < AbstractModel
  belongs_to :imageable, polymorphic: true, inverse_of: :pictures
  mount_uploader :image, ImageUploader

class User < AbstractModel
  has_one :picture, class_name: Picture, as: :imageable, dependent: :destroy, inverse_of: :imageable
  accepts_nested_attributes_for :picture

Here is state of model in for_user? method

[5] pry(#<ImageUploader>)> ap model
Изображение для  {
                :id => nil,
             :image => #<ImageUploader:0x005583dee8f938 @model=#<Picture id: nil, image: nil, imageable_id: nil, imageable_type: nil, created_at: nil, updated_at: nil>, @mounted_as=:image, @cache_id="1463023347-21567-5096", @filename="rootp_HE_pause-20160113_141810.jpg", @original_filename="rootp_HE_pause-20160113_141810.jpg", @file=#<CarrierWave::SanitizedFile:0x005583dee8cf30 @file="/home/kvokka/proj/volunteers/public/uploads/tmp/1463023347-21567-5096/rootp_HE_pause-20160113_141810.jpg", @original_filename=nil, @content_type="image/jpeg">, @versions={:mini=>#<ImageUploader::Uploader47012442238420:0x005583dee724a0 @model=#<Picture id: nil, image: nil, imageable_id: nil, imageable_type: nil, created_at: nil, updated_at: nil>, @mounted_as=:image>, :xs=>#<ImageUploader::Uploader47012435752020:0x005583dee72450 @model=#<Picture id: nil, image: nil, imageable_id: nil, imageable_type: nil, created_at: nil, updated_at: nil>, @mounted_as=:image>, :avatar=>#<ImageUploader::Uploader47012437005860:0x005583dee72428 @model=#<Picture id: nil, image: nil, imageable_id: nil, imageable_type: nil, created_at: nil, updated_at: nil>, @mounted_as=:image>}>,
      :imageable_id => nil,
    :imageable_type => nil,
        :created_at => nil,
        :updated_at => nil

UPDATE2

I've thried with inverse_of: :picture and with inverse_of: :pictures. Syntax error. It runs with inverse_of: :imageable, but the result is the same.

last version of ImageUploader (I've cut it for brevity) is

class ImageUploader < CarrierWave::Uploader::Base
  version :xs do
    process resize_to_fit: [100, 100]
  end

  version :avatar, if: :for_user? do
    process resize_to_fit: [360, 360]
  end

  private

    def for_user?(_)
      model.imageable.is_a? User
    end
end

UPDATE3

I've made some console moves, so you can see, that model accepts nested attributes. In case of has_one relation it works without #inverse_of method too (and this moment is tricky for me). So, the log:

[43] pry(main)> ap v = VolunteerCenter.create(title: 'tst', address:'tst', city: City.first, phone:'123456',email: 'mm@mm.ru', description: 'ololo', link: 'http://ddd.ru', picture: (Picture.new( remote_image_url: 'https://retina.news.mail.ru/prev780x440/pic/e5/35/image25749462_adfc024a9b54b718c1a755445661b099.jpg')))
  City Load (0.8ms)  SELECT  "cities".* FROM "cities"  ORDER BY "cities"."id" ASC LIMIT 1
   (0.2ms)  BEGIN
  SQL (0.4ms)  INSERT INTO "volunteer_centers" ("title", "address", "city_id", "phone", "email", "description", "link", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"  [["title", "tst"], ["address", "tst"], ["city_id", 1], ["phone", "123456"], ["email", "mm@mm.ru"], ["description", "ololo"], ["link", "http://ddd.ru"], ["created_at", "2016-05-12 12:13:44.417945"], ["updated_at", "2016-05-12 12:13:44.417945"]]
  SQL (0.3ms)  INSERT INTO "pictures" ("image", "imageable_type", "imageable_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["image", "image25749462_adfc024a9b54b718c1a755445661b099.jpg"], ["imageable_type", "VolunteerCenter"], ["imageable_id", 11], ["created_at", "2016-05-12 12:13:44.421458"], ["updated_at", "2016-05-12 12:13:44.421458"]]
   (2.2ms)  COMMIT
tst {
             :id => 11,
          :title => "tst",
     :created_at => Thu, 12 May 2016 12:13:44 UTC +00:00,
     :updated_at => Thu, 12 May 2016 12:13:44 UTC +00:00,
        :address => "tst",
        :city_id => 1,
          :phone => "123456",
          :email => "mm@mm.ru",
    :description => "ololo",
        :vk_link => nil,
           :link => "http://ddd.ru"
}
=> nil
[44] pry(main)> ap v.picture
  VolunteerCenter Load (0.6ms)  SELECT  "volunteer_centers".* FROM "volunteer_centers" WHERE "volunteer_centers"."id" = $1 LIMIT 1  [["id", 11]]
Изображение для tst {
                :id => 120,
             :image => #<ImageUploader:0x0055596544d928 @model=#<Picture id: 120, image: "image25749462_adfc024a9b54b718c1a755445661b099.jpg", imageable_id: 11, imageable_type: "VolunteerCenter", created_at: "2016-05-12 12:13:44", updated_at: "2016-05-12 12:13:44">, @mounted_as=:image, @cache_id=nil, @filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @file=#<CarrierWave::SanitizedFile:0x00555963f0c690 @file="/home/kvokka/proj/volunteers/public/uploads/picture/image/120/image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename=nil, @content_type="image/jpeg">, @versions={:mini=>#<ImageUploader::Uploader46921211280280:0x00555966c55890 @model=#<Picture id: 120, image: "image25749462_adfc024a9b54b718c1a755445661b099.jpg", imageable_id: 11, imageable_type: "VolunteerCenter", created_at: "2016-05-12 12:13:44", updated_at: "2016-05-12 12:13:44">, @mounted_as=:image, @parent_cache_id="1463055224-8966-4735", @cache_id=nil, @filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @file=#<CarrierWave::SanitizedFile:0x00555966e89620 @file="/home/kvokka/proj/volunteers/public/uploads/picture/image/120/mini_image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename=nil, @content_type="image/jpeg">, @versions={}, @storage=#<CarrierWave::Storage::File:0x00555966e8a4a8 @uploader=#<ImageUploader::Uploader46921211280280:0x00555966c55890 ...>>>, :xs=>#<ImageUploader::Uploader46921211274220:0x00555966c55868 @model=#<Picture id: 120, image: "image25749462_adfc024a9b54b718c1a755445661b099.jpg", imageable_id: 11, imageable_type: "VolunteerCenter", created_at: "2016-05-12 12:13:44", updated_at: "2016-05-12 12:13:44">, @mounted_as=:image, @parent_cache_id="1463055224-8966-4735", @cache_id=nil, @filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @file=#<CarrierWave::SanitizedFile:0x005559659d60c0 @file="/home/kvokka/proj/volunteers/public/uploads/picture/image/120/xs_image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename=nil, @content_type="image/jpeg">, @versions={}, @storage=#<CarrierWave::Storage::File:0x005559659d7010 @uploader=#<ImageUploader::Uploader46921211274220:0x00555966c55868 ...>>>, :avatar==>#<ImageUploader::Uploader46921211257860:0x00555966c55840 @model=#<Picture id: 120, image: "image25749462_adfc024a9b54b718c1a755445661b099.jpg", imageable_id: 11, imageable_type: "VolunteerCenter", created_at: "2016-05-12 12:13:44", updated_at: "2016-05-12 12:13:44">, @mounted_as=:image, @parent_cache_id="1463055224-8966-4735", @cache_id=nil, @filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename="image25749462_adfc024a9b54b718c1a755445661b099.jpg", @file=#<CarrierWave::SanitizedFile:0x00555963379328 @file="/home/kvokka/proj/volunteers/public/uploads/picture/image/120/avatar=_image25749462_adfc024a9b54b718c1a755445661b099.jpg", @original_filename=nil, @content_type="image/jpeg">, @versions={}, @storage=#<CarrierWave::Storage::File:0x0055596337a390 @uploader=#<ImageUploader::Uploader46921211257860:0x00555966c55840 ...>>>}, @storage=#<CarrierWave::Storage::File:0x00555965d85fb8 @uploader=#<ImageUploader:0x0055596544d928 ...>>>,
      :imageable_id => 11,
    :imageable_type => "VolunteerCenter",
        :created_at => Thu, 12 May 2016 12:13:44 UTC +00:00,
        :updated_at => Thu, 12 May 2016 12:13:44 UTC +00:00
}

UPDATE4

This happents when I run with pry in callback method.

class ImageUploader < CarrierWave::Uploader::Base
  version :avatar, if: :for_user? do
    puts 'This line never run'
    process resize_to_fit: [360, 360]
  end

  private

    def for_user?(picture)
      binding.pry
      model.imageable.is_a? User
    end

I switch off all validations for clarify. This call back is invoked 3 times, and in first 2 User instance is not ready, and after the last one the callback do not work.

[46] pry(main)> ap u = User.create(email: 'example@test.com', password: '123456', password_confirmation: '123456', picture: (Picture.create remote_image_url: 'http://onrails.club/uploads/default/29/31f7627609164af8.png'))

From: /home/kvokka/proj/volunteers/app/uploaders/image_uploader.rb @ line 68 ImageUploader#for_user?:

    66: def for_user?(_)
    67:   binding.pry
 => 68:   model.imageable.is_a? User
    69: end

@cache_id                "1463060913-8966-3349"
@file                    #<CarrierWave::SanitizedFile:0x00555963268330 @file="/hom...
@filename                "31f7627609164af8.png"
@model                   #<Picture id: nil, image: nil, imageable_id: nil, imageab...
@mounted_as              :image
@original_filename       "31f7627609164af8.png"
@versions                {:mini=>#<ImageUploader::Uploader46921199022860:0x0055596...
[1] pry(#<ImageUploader>)> 

From: /home/kvokka/proj/volunteers/app/uploaders/image_uploader.rb @ line 68 ImageUploader#for_user?:

    66: def for_user?(_)
    67:   binding.pry
 => 68:   model.imageable.is_a? User
    69: end

@cache_id                "1463060913-8966-3349"
@file                    #<CarrierWave::SanitizedFile:0x00555963268330 @file="/hom...
@filename                "31f7627609164af8.png"
@model                   #<Picture id: nil, image: nil, imageable_id: nil, imageab...
@mounted_as              :image
@original_filename       "31f7627609164af8.png"
@versions                {:mini=>#<ImageUploader::Uploader46921199022860:0x0055596...
[1] pry(#<ImageUploader>)> 
   (0.3ms)  BEGIN
  SQL (0.7ms)  INSERT INTO "pictures" ("image", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["image", "31f7627609164af8.png"], ["created_at", "2016-05-12 13:48:40.071387"], ["updated_at", "2016-05-12 13:48:40.071387"]]

From: /home/kvokka/proj/volunteers/app/uploaders/image_uploader.rb @ line 68 ImageUploader#for_user?:

    66: def for_user?(_)
    67:   binding.pry
 => 68:   model.imageable.is_a? User
    69: end

@cache_id                nil
@file                    #<CarrierWave::SanitizedFile:0x005559639c6348 @file="/hom...
@filename                "31f7627609164af8.png"
@model                   #<Picture id: 112, image: "31f7627609164af8.png", imageab...
@mounted_as              :image
@original_filename       "31f7627609164af8.png"
@storage                 #<CarrierWave::Storage::File:0x00555963b5add0 @uploader=#...
@versions                {:mini=>#<ImageUploader::Uploader46921199022860:0x0055596...
[1] pry(#<ImageUploader>)> 
   (2.9ms)  COMMIT
   (0.2ms)  BEGIN
  User Exists (0.7ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = 'example@test.com' LIMIT 1
  SQL (0.4ms)  INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["email", "example@test.com"], ["encrypted_password", "$2a$11$qq8tVgzISJguKz7oVJWyVO45vW/ujSEsi/ow29w78nC9ByOdjrPYK"], ["created_at", "2016-05-12 13:48:41.761836"], ["updated_at", "2016-05-12 13:48:41.761836"]]
  SQL (0.4ms)  UPDATE "pictures" SET "imageable_type" = $1, "imageable_id" = $2, "updated_at" = $3 WHERE "pictures"."id" = $4  [["imageable_type", "User"], ["imageable_id", 31], ["updated_at", "2016-05-12 13:48:41.764681"], ["id", 112]]
   (2.7ms)  COMMIT
 ,  {
                           :id => 31,
                        :email => "example@test.com",
           :encrypted_password => "$2a$11$qq8tVgzISJguKz7oVJWyVO45vW/ujSEsi/ow29w78nC9ByOdjrPYK",
         :reset_password_token => nil,
       :reset_password_sent_at => nil,
          :remember_created_at => nil,
                :sign_in_count => 0,
           :current_sign_in_at => nil,
              :last_sign_in_at => nil,
           :current_sign_in_ip => nil,
              :last_sign_in_ip => nil,
                   :created_at => Thu, 12 May 2016 13:48:41 UTC +00:00,
                   :updated_at => Thu, 12 May 2016 13:48:41 UTC +00:00,
                         :name => nil,
                      :surname => nil,
                        :phone => nil,
                          :dob => nil,
                       :gender => nil,
                       :height => nil,
                        :about => nil,
        :information_source_id => nil,
               :institution_id => nil,
              :clothes_size_id => nil,
                      :city_id => nil,
          :volunteer_center_id => nil,
                     :blood_id => nil,
               :vkontakte_link => nil,
    :medical_contraindications => nil
}
=> nil

The question is still open

Mike Belyakov
  • 1,355
  • 1
  • 13
  • 24

1 Answers1

0

How I can fetch the picture owner model here?

Assuming that you are referring to a User record when you say "picture owner model", then you could do the following

class ImageUploader < CarrierWave::Uploader::Base
  ...
  def for_user?(picture)
    user = model.imageable
    user.is_a? User
    # => true
  end
  ...
end

From docs:

  • picture parameter is the file object being uploaded (this is not an instance of your Picture model)

  • model is the reference to an instance object where you use this ImageUploader, which in your case refers to an instance of Picture model. Take note that this is different to picture above.

Now that you know model is an instance of Picture, and that Picture belongs_to :imageable, and that :imageable is a polymorphic association which is actually a User record in your case, then you can access the associated record with my answer above. Take note however, that it may not always be a User record because it is a polymorphic association.

UPDATE

I'm guessing model.imageable is not working because the imageable record is nil after being set up from nested params.

Can you try

class Picture < AbstractModel
  belongs_to :imageable, polymorphic: true, inverse_of: :picture # WITHOUT s

UPDATE

Looks like inverse_of doesn't work with polymorphic association (here and here)

There are a few limitations to inverse_of support:

  • They do not work with :through associations.
  • They do not work with :polymorphic associations.
  • They do not work with :as associations.
  • For belongs_to associations, has_many inverse associations are ignored.

P.S. This might be tricky. If I have the time later, I will try to reproduce the problem.

Community
  • 1
  • 1
Jay-Ar Polidario
  • 6,463
  • 14
  • 28
  • Thank you, but it did not help. I've updated the question and added some details, so, it may be useful. Your idea will fork only for updating the image, and for new one I'll capture all versions. – Mike Belyakov May 12 '16 at 03:29
  • Thank you. I've thried :picture and :pictures. Syntax error. It runs with inverse_of: :imageable, but the result is the same. – Mike Belyakov May 12 '16 at 10:58
  • Just one thing I want to verify. Are you sure that the associated Imageable record gets "built" in the controller? You can check by doing the following inside `def create`: `@picture = Picture.new(picture_params); puts ap @picture.imageable` Can you show me what the `puts` output to and make sure that this imageable object gets "built" from the nested params? – Jay-Ar Polidario May 12 '16 at 11:30
  • If `@picture.imageable` does not get "built", you may try [this](http://stackoverflow.com/questions/3969025/accepts-nested-attributes-for-with-belongs-to-polymorphic) first if it will work. – Jay-Ar Polidario May 12 '16 at 11:45
  • It is strange, but it works and do not works in the same time. I've updates the question. and as contrargument follow this link, here it works, as i think. https://github.com/sferik/rails_admin/wiki/Polymorphic-belongs-to-association – Mike Belyakov May 12 '16 at 12:19
  • Can you add this to `def for_user?(picture)`, `puts ap model.imageable`, then can you redo the console-commands you did in Update3? I just want to know if you can access the `.imageable` provided you manually use `.create(... picture: Picture.new())` instead of `.create(user_params)` because my current guess is that `.imageable` doesn't get "build" because you might be missing to include it in nested strong params. Showing your controller code would be helpful in this regard. – Jay-Ar Polidario May 12 '16 at 13:22
  • I updated my answer which now is referencing the official API docs regarding inverse_of not supported for polymorphic associations. There are two things I am not sure yet which might be causing the actual problem. 1) Controller-side didn't "build" the associated `.imageable` object maybe because you forgot to include (example) `permit(picture_attributes: [:attribute1, :attribute2]` as part of strong params, 2) If controller-side is really working properly: that `.imageable` object gets "build" properly, then the problem now might be because `inverse_of` does not support polymorphic association – Jay-Ar Polidario May 12 '16 at 13:32
  • Actually, a do no use it in controller directly. It is used by activeadmin cms or devise gem. There is no problem with permitted params, because for test I enabled everything. I'll push in the question pry log in few minutes. – Mike Belyakov May 12 '16 at 13:34
  • Really, i have really the same result with or with out including :inverse_of in options. I understand, that it is needed option in this case, but do not know, why it works with out it – Mike Belyakov May 12 '16 at 13:37
  • you said "why it works with out [inverse_of]?" or did you mean "why it does not work with out [inverse_of]?" – Jay-Ar Polidario May 12 '16 at 13:39
  • last run i;ve made with out [inverse_of], and this option really does not make sense. And I do not understand also, why it works with OUT this option. I thought, that in this case this option is mandatory. I'll plug in this topic to carrierwave repo, so I think the problem is in it. May be they help. (look at update4) – Mike Belyakov May 12 '16 at 13:58
  • From your Update4, looks like you can now access the user record through `model.imageable`. You are right that `inverse_of` is not needed to make this work as you just observed in Update4. I only added `inverse_of` just in case it might be causing the problem. Now, your main problem then is that this does not seem to work for activeadmin / cms. This will be a separate problem. – Jay-Ar Polidario May 12 '16 at 14:05
  • But anyway it should work in console. If I stub the method #for_user? with `true` all works, so the main reason is, that the Picture is not created in needed time. I's seems tricky, but I'm too tired for today and I have to sleep, and, try to solve it tomorrow (or surrender and begin trying other file processors). – Mike Belyakov May 12 '16 at 14:41