Rails Model.update deletes nested attachment - ruby-on-rails

I have a Rails 6 app.
User has_one Shop
Shop has_one_attached Logo
User.update with nested_attributes purges the User.shop.company_logo.
class User
has_one :shop, dependent: :destroy # delete Shop if user gets deleted
accepts_nested_attributes_for :shop, # also tried reject_if: :all_blank
end
class Shop
belongs_to :user
has_one_attached :company_logo do |attachable|
attachable.variant :thumbnail, resize_to_fill: [100, 100]
end
end
# creating a User
User.create({name: "Test"})
# creating a Shop
logo = File.open(Rails.root.join('spec', 'fixtures', 'files', '400x400.png'))
Shop.create(user_id: User.last.id, name: "TestShop", company_logo: logo)
User.last.shop.company_logo.attached? # returns TRUE !!!
# Update User (deletes / detaches company_logo)
params = {name: "TestUpdateName", shop: {name: "TestUpdateShop"}}
User.last.update(params)
User.last.shop.company_logo.attached? # returns FALSE !!!
Console output:
[ActiveJob] Enqueued ActiveStorage::PurgeJob (Job ID: caebfa82-7c41-4eb9-b295-c213449e0a7e) to Async(active_storage_purge) with arguments: #<GlobalID:0x00007fcf6ae179e0 #uri=#<URI::GID gid://meta-shop/ActiveStorage::Blob/81>>
ActiveStorage::Blob Load (1.7ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2 [["id", 81], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] Performing ActiveStorage::PurgeJob (Job ID: caebfa82-7c41-4eb9-b295-c213449e0a7e) from Async(active_storage_purge) enqueued at 2021-09-25T10:01:32Z with arguments: #<GlobalID:0x00007fcf5a8856b8 #uri=#<URI::GID gid://meta-shop/ActiveStorage::Blob/81>>
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] TRANSACTION (2.0ms) BEGIN
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] ActiveStorage::Attachment Exists? (2.1ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2 [["blob_id", 81], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] ActiveStorage::Attachment Load (2.8ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 81], ["record_type", "ActiveStorage::Blob"], ["name", "preview_image"], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] ActiveStorage::Blob Destroy (8.8ms) DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 81]]
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] TRANSACTION (5.1ms) COMMIT
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] S3 Storage (224.0ms) Deleted file from key: mx5g91bi8qwgvh6a8vzh65598kd3
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] S3 Storage (311.1ms) Deleted files by key prefix: variants/mx5g91bi8qwgvh6a8vzh65598kd3/
[ActiveJob] [ActiveStorage::PurgeJob] [caebfa82-7c41-4eb9-b295-c213449e0a7e] Performed ActiveStorage::PurgeJob (Job ID: caebfa82-7c41-4eb9-b295-c213449e0a7e) from Async(active_storage_purge) in 568.39ms
How can I persist the User.shop.company_logo when updating with nested_attributes ?

I found a solution:
My Shop model was replaced when updating through the parent User model. But there is an option which can be added on "one-to-one" relations like has_one. It's the update_only option, which is false by default. When using this, the nested model get updated and not replaced when using update_only: false
accepts_nested_attributes_for :shop, update_only: true
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for

Related

Partially working simple image upload using Active Storage

Any help or guidance would be most appreciated.
I've followed the Rails Guide, however, and I can upload an image to a model. However, it produces an error.
I've copied what the error suggests and that produces another error and does not upload an image.
Many, many thanks in advance!
This works but produces an error post form submission. If I hit back and then visit the specific prod id product#show the image has uploaded.
Error
NameError in ProductsController#create
undefined local variable or method `product' for # Did you mean? #product
Products.rb
def create
#list = List.find(params[:list_id])
#product = #list.products.create(product_params)
product.hero.attach(params[:hero])
redirect_to list_path(#list)
end
This does not work however, looks syntactically correct as the "#product" model attaches to the :hero .
Products.rb
def create
#list = List.find(params[:list_id])
#product = #list.products.create(product_params)
#product.hero.attach(params[:hero])
redirect_to list_path(#list)
end
creates the product
refreshes the screen back to the product list so it looks correct.
This breaks producing two errors:
The first error on products#show
ArgumentError in Products#show
Showing /Users/user/rubyonrails/shopping/app/views/products/show.html.erb where line #2 raised:
Can't resolve image into URL: to_model delegated to attachment, but the attachment is nil
Second terminal output presents the upload seems to work then something called Active Storage Purge fires up removing it?
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2 [["id", 7], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Performing ActiveStorage::PurgeJob (Job ID: e9b35a62-ff6c-4a38-8946-3aa9c19668ef) from Async(active_storage_purge) enqueued at 2020-04-15T11:31:41Z with arguments: #<GlobalID:0x00007f92b1da3d60 #uri=#<URI::GID gid://shopping/ActiveStorage::Blob/7>>
[ActiveJob] [ActiveStorage::AnalyzeJob] [b724849f-6993-4130-bef1-a0f8837a3171] (6.4ms) COMMIT
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] (0.8ms) BEGIN
[ActiveJob] [ActiveStorage::AnalyzeJob] [b724849f-6993-4130-bef1-a0f8837a3171] Performed ActiveStorage::AnalyzeJob (Job ID: b724849f-6993-4130-bef1-a0f8837a3171) from Async(active_storage_analysis) in 15.88ms
Started GET "/lists/9" for ::1 at 2020-04-15 12:31:41 +0100
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Attachment Exists? (0.5ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2 [["blob_id", 7], ["LIMIT", 1]]
Processing by ListsController#show as HTML
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Attachment Load (1.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 7], ["record_type", "ActiveStorage::Blob"], ["name", "preview_image"], ["LIMIT", 1]]
Parameters: {"id"=>"9"}
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Blob Destroy (1.0ms) DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
List Load (0.9ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
↳ app/controllers/lists_controller.rb:7:in `show'
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
↳ app/controllers/lists_controller.rb:8:in `show'
Rendering lists/show.html.erb within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/views/lists/show.html.erb:2
Product Exists? (0.2ms) SELECT 1 AS one FROM "products" WHERE "products"."list_id" = $1 LIMIT $2 [["list_id", 9], ["LIMIT", 1]]
↳ app/views/lists/show.html.erb:5
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."list_id" = $1 [["list_id", 9]]
↳ app/views/lists/show.html.erb:6
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] (6.4ms) COMMIT
(0.8ms) SELECT COUNT(*) FROM "products" WHERE "products"."list_id" = $1 [["list_id", 9]]
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Disk Storage (0.2ms) Deleted file from key: 31cfuow9pj6vjqhq8i479fdxf1lc
↳ app/controllers/application_controller.rb:5:in `product_list_size?'
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Disk Storage (0.1ms) Deleted files by key prefix: variants/31cfuow9pj6vjqhq8i479fdxf1lc/
Rendered lists/show.html.erb within layouts/application (Duration: 9.5ms | Allocations: 7278)
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Performed ActiveStorage::PurgeJob (Job ID: e9b35a62-ff6c-4a38-8946-3aa9c19668ef) from Async(active_storage_purge) in 22.5ms
[Webpacker] Everything's up-to-date. Nothing to do
List item
Egg on my face.
The exclamation mark omission seems to be the issue.
from
#product = #list.products.create(product_params)
to this and it works
#product = #list.products.create!(product_params)

Rails 5 : Devise Oauth Referral param not saved

I have an app where user can register using email, facebook or google, also I have small referral system where user can share link and earn points.
Now everything works perfectly registering and login. But referral system works only when creating account using social networks.
the following is my code :
omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, event: :authentication
#this will throw if #user is not activated
set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
# google callback
def google_oauth2
#user = User.from_omniauth(request.env['omniauth.auth'])
if #user.persisted?
sign_in_and_redirect #user
set_flash_message(:notice, :success, kind: 'Google') if is_navigational_format?
else
flash[:error] = 'There was a problem signing you in through Google. Please register or try signing in later.'
redirect_to new_user_registration_url
end
end
def failure
flash[:error] = 'There was a problem signing you in. Please register or try signing in later.'
redirect_to new_user_registration_url
end
end
registrations_controller.rb
def build_resource(hash = {})
super
if cookies[:referral_code] && referrer = User.find_by(referral_code: cookies[:referral_code])
self.resource.referred_by = referrer
end
end
user.rb
before_validation :set_referral_code
validates :referral_code, uniqueness: true
def set_referral_code
loop do
self.referral_code = SecureRandom.hex(6)
break unless self.class.exists?(referral_code: referral_code)
end
end
application_controller.rb
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :set_referral_cookie
def set_referral_cookie
if params[:ref]
cookies[:referral_code] = {
value: params[:ref],
expires: 30.days.from_now,
}
end
end
I built my system using help of this repository
gorails referral system
Edit
Started GET "/?ref=6c28f9668715" for 127.0.0.1 at 2019-01-27 21:23:32 +0100
Processing by HomeController#index as HTML
Parameters: {"ref"=>"6c28f9668715"}
Rendering home/index.html.erb within layouts/application
Rendered home/index.html.erb within layouts/application (0.6ms)
Rendered shared/_navbar.html.erb (1.4ms)
Completed 200 OK in 125ms (Views: 90.7ms | ActiveRecord: 0.0ms)
Started GET "/users/sign_up" for 127.0.0.1 at 2019-01-27 21:23:34 +0100
Processing by Users::RegistrationsController#new as HTML
User Load (1.6ms) SELECT "users".* FROM "users" WHERE "users"."referral_code" = $1 LIMIT $2 [["referral_code", "6c28f9668715"], ["LIMIT", 1]]
↳ app/controllers/users/registrations_controller.rb:7
Rendering users/registrations/new.html.erb within layouts/application
Rendered users/registrations/new.html.erb within layouts/application (6.7ms)
Rendered shared/_navbar.html.erb (1.2ms)
Completed 200 OK in 300ms (Views: 142.0ms | ActiveRecord: 1.6ms)
Started GET "/users/auth/facebook" for 127.0.0.1 at 2019-01-27 21:23:37 +0100
I, [2019-01-27T21:23:37.266108 #67828] INFO -- omniauth: (facebook) Request phase initiated.
Started GET "/users/auth/facebook" for 127.0.0.1 at 2019-01-27 21:23:38 +0100
I, [2019-01-27T21:23:38.488342 #67828] INFO -- omniauth: (facebook) Request phase initiated.
Started GET "/users/auth/facebook/callback?code=AQAemLam_HRACk1q5NTfix5Sve6rA1fsDD5z_21vKpDlaTq4hfuXM2Oh_CThPspwk1BIg4Tjc1bm0UOcXLo_X0XGVI8XdLsirhPV6wKnGiCO3uU3l4y6y31qnhC1xjzd-21wx_cWVO-ipPCzrZ8kqWCdvQrxxKOQXMj10LsKlTAbuSqMEpx90XvcZw3RAYGLSiEFQGJSgCpABpboh_n_ewjTbfbTB01JATW6hM9Wy8iN1AQLpXrRgOZ-5P1NdowqdHdjU420N6QoB7R9tyHXegioQ47J8cjgCMUFwDPi_T--zHK6_-sIkW_QE6P5ryot1qHxzHpOASvx46WHvJun5_Yh&state=bd3dfbf7e7a5c83c42d1d754149d7be5ea7f39b6d5b99b28" for 127.0.0.1 at 2019-01-27 21:23:39 +0100
I, [2019-01-27T21:23:39.483990 #67828] INFO -- omniauth: (facebook) Callback phase initiated.
Processing by Users::OmniauthCallbacksController#facebook as HTML
Parameters: {"code"=>"AQAemLam_HRACk1q5NTfix5Sve6rA1fsDD5z_21vKpDlaTq4hfuXM2Oh_CThPspwk1BIg4Tjc1bm0UOcXLo_X0XGVI8XdLsirhPV6wKnGiCO3uU3l4y6y31qnhC1xjzd-21wx_cWVO-ipPCzrZ8kqWCdvQrxxKOQXMj10LsKlTAbuSqMEpx90XvcZw3RAYGLSiEFQGJSgCpABpboh_n_ewjTbfbTB01JATW6hM9Wy8iN1AQLpXrRgOZ-5P1NdowqdHdjU420N6QoB7R9tyHXegioQ47J8cjgCMUFwDPi_T--zHK6_-sIkW_QE6P5ryot1qHxzHpOASvx46WHvJun5_Yh", "state"=>"bd3dfbf7e7a5c83c42d1d754149d7be5ea7f39b6d5b99b28"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2 ORDER BY "users"."id" ASC LIMIT $3 [["provider", "facebook"], ["uid", "311741132783400"], ["LIMIT", 1]]
↳ app/models/user.rb:60
Disk Storage (5.7ms) Uploaded file to key: Sp8DiDxRWFn3uUfGTJJHdABc (checksum: tjmoDYRADOqNyXiWy90gxg==)
(0.3ms) BEGIN
↳ app/models/user.rb:66
ActiveStorage::Blob Create (44.9ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["key", "Sp8DiDxRWFn3uUfGTJJHdABc"], ["filename", "avatar.jpg"], ["content_type", "image/jpeg"], ["metadata", "{\"identified\":true}"], ["byte_size", 1640], ["checksum", "tjmoDYRADOqNyXiWy90gxg=="], ["created_at", "2019-01-27 20:23:42.423862"]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/persistence.rb:12
(0.9ms) COMMIT
↳ app/models/user.rb:66
(0.2ms) BEGIN
↳ app/models/user.rb:66
(0.2ms) COMMIT
↳ app/models/user.rb:66
(0.2ms) BEGIN
↳ app/models/user.rb:60
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2 AND "users"."referral_code" = $3 LIMIT $4 [["provider", "facebook"], ["uid", "311741132783400"], ["referral_code", "2925f372e912"], ["LIMIT", 1]]
↳ app/models/user.rb:79
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "qubitam#gmail.com"], ["LIMIT", 1]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/rails5_1/uniqueness_validator.rb:38
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."referral_code" = $1 LIMIT $2 [["referral_code", "2925f372e912"], ["LIMIT", 1]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/rails5_1/uniqueness_validator.rb:38
User Create (2.5ms) INSERT INTO "users" ("email", "encrypted_password", "name", "created_at", "updated_at", "provider", "uid", "referral_code") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id" [["email", "qubitam#gmail.com"], ["encrypted_password", "$2a$11$JUxrDl1I4uDQwxi3yRVrnuqRJNYQJA8BcvLrmAj29nwBggJknrP3i"], ["name", "Abdelmoumin Mokhtari"], ["created_at", "2019-01-27 20:23:42.483417"], ["updated_at", "2019-01-27 20:23:42.483417"], ["provider", "facebook"], ["uid", "311741132783400"], ["referral_code", "2925f372e912"]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/persistence.rb:12
ActiveStorage::Attachment Create (0.7ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "image"], ["record_type", "User"], ["record_id", 13], ["blob_id", 10], ["created_at", "2019-01-27 20:23:42.487979"]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/persistence.rb:12
User Update (41.5ms) UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2 [["updated_at", "2019-01-27 20:23:42.490719"], ["id", 13]]
↳ app/models/user.rb:60
(40.9ms) COMMIT
↳ app/models/user.rb:60
[ActiveJob] Enqueued ActiveStorage::AnalyzeJob (Job ID: 1f7ce124-dc8b-4302-912e-7b1138656e35) to Async(default) with arguments: #<GlobalID:0x00007fa636abf0e8 #uri=#<URI::GID gid://classifyads/ActiveStorage::Blob/10>>
(1.0ms) BEGIN
↳ app/controllers/users/omniauth_callbacks_controller.rb:8
ActiveStorage::Blob Load (0.4ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2 [["id", 10], ["LIMIT", 1]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] Performing ActiveStorage::AnalyzeJob (Job ID: 1f7ce124-dc8b-4302-912e-7b1138656e35) from Async(default) with arguments: #<GlobalID:0x00007fa636aac790 #uri=#<URI::GID gid://classifyads/ActiveStorage::Blob/10>>
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] Disk Storage (0.2ms) Downloaded file from key: Sp8DiDxRWFn3uUfGTJJHdABc
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] Skipping image analysis because the mini_magick gem isn't installed
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] (0.2ms) BEGIN
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] ↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] ActiveStorage::Blob Update (0.6ms) UPDATE "active_storage_blobs" SET "metadata" = $1 WHERE "active_storage_blobs"."id" = $2 [["metadata", "{\"identified\":true,\"analyzed\":true}"], ["id", 10]]
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] ↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/persistence.rb:7
User Update (82.8ms) UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "last_sign_in_at" = $3, "current_sign_in_ip" = $4, "last_sign_in_ip" = $5, "updated_at" = $6 WHERE "users"."id" = $7 [["sign_in_count", 1], ["current_sign_in_at", "2019-01-27 20:23:42.581519"], ["last_sign_in_at", "2019-01-27 20:23:42.581519"], ["current_sign_in_ip", "127.0.0.1/32"], ["last_sign_in_ip", "127.0.0.1/32"], ["updated_at", "2019-01-27 20:23:42.584122"], ["id", 13]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/bundler/gems/globalize-3fe2f93ab2d0/lib/patches/active_record/persistence.rb:7
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] (61.8ms) COMMIT
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] ↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
[ActiveJob] [ActiveStorage::AnalyzeJob] [1f7ce124-dc8b-4302-912e-7b1138656e35] Performed ActiveStorage::AnalyzeJob (Job ID: 1f7ce124-dc8b-4302-912e-7b1138656e35) from Async(default) in 122.16ms
(79.1ms) COMMIT
↳ app/controllers/users/omniauth_callbacks_controller.rb:8
Redirected to http://localhost:3000/users/edit
Completed 302 Found in 2383ms (ActiveRecord: 297.1ms)
Started GET "/users/edit" for 127.0.0.1 at 2019-01-27 21:23:42 +0100
Processing by Users::RegistrationsController#edit as HTML
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 13], ["LIMIT", 1]]
↳ /Users/qubitam/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Rendering users/registrations/edit.html.erb within layouts/application
ActiveStorage::Attachment Load (0.6ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 13], ["record_type", "User"], ["name", "image"], ["LIMIT", 1]]
↳ app/views/users/registrations/edit.html.erb:8
ActiveStorage::Blob Load (0.9ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2 [["id", 10], ["LIMIT", 1]]
↳ app/views/users/registrations/edit.html.erb:8
Rendered users/registrations/edit.html.erb within layouts/application (14.4ms)
Rendered shared/_navbar.html.erb (3.9ms)
Completed 200 OK in 105ms (Views: 100.1ms | ActiveRecord: 2.2ms)

Associations cause "EMPTY" in Active Admin

So I'm learning more about belongs_to and has_many Associations in Rails and am combining it with ActiveAdmin.
I have created a Model "Semester" and a Model "Field". A Semester has many Fields and a Field belongs to Semester.
My field Class looks like this:
class Field < ApplicationRecord
belongs_to :semester
accepts_nested_attributes_for :semester, allow_destroy: true
end
and my Semester class looks like this:
class Semester < ApplicationRecord
has_many :fields
accepts_nested_attributes_for :fields, allow_destroy: true
end
Now I registered the Models with active admin with the following two files:
ActiveAdmin.register Field do
permit_params :name, semesters_attributes: [:name]
end
and
ActiveAdmin.register Semester do
permit_params :name, :fields, fields_attributes: [ :field_id, :name]
end
And now there are two issues that come up upon proceeding that I absolutely can not ged rid off:
1) If I do not add optional: true after belongs_to :semester I will get an error message "must exist" upon trying to create a new Field with a respective Semester.
2) If I do add optional: true after belongs_to :semester I will be able to create a new Field but the "Semester" will just be "EMPTY" in the new field.
The console output of case 2) will look like this:
Started POST "/admin/fields" for 127.0.0.1 at 2018-08-17 15:23:54 +0200
Processing by Admin::FieldsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+GPjjNPOv9GsjXnEtEjBcC0xUMHKKC+YpFLfiUFUOgsgBJ+pLCucscrN0YaTk551GFp4K5lBEI2RW1clw2vCWw==", "field"=>{"semester_id"=>"2", "name"=>"MAVT"}, "commit"=>"Create Field"}
AdminUser Load (0.1ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = ? ORDER BY "admin_users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Unpermitted parameter: :semester_id
(0.0ms) begin transaction
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Field Create (0.6ms) INSERT INTO "fields" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "MAVT"], ["created_at", "2018-08-17 13:23:54.026418"], ["updated_at", "2018-08-17 13:23:54.026418"]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
(12.2ms) commit transaction
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Redirected to http://0.0.0.0:3000/admin/fields/22
Completed 302 Found in 22ms (ActiveRecord: 13.0ms)
Started GET "/admin/fields/22" for 127.0.0.1 at 2018-08-17 15:23:54 +0200
Processing by Admin::FieldsController#show as HTML
Parameters: {"id"=>"22"}
AdminUser Load (0.2ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = ? ORDER BY "admin_users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Field Load (0.1ms) SELECT "fields".* FROM "fields" WHERE "fields"."id" = ? LIMIT ? [["id", 22], ["LIMIT", 1]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Rendering /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.3.1/app/views/active_admin/resource/show.html.arb
(0.1ms) SELECT COUNT(*) FROM "active_admin_comments" WHERE "active_admin_comments"."resource_type" = ? AND "active_admin_comments"."resource_id" = ? AND "active_admin_comments"."namespace" = ? [["resource_type", "Field"], ["resource_id", 22], ["namespace", "admin"]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
ActiveAdmin::Comment Exists (0.1ms) SELECT 1 AS one FROM "active_admin_comments" WHERE "active_admin_comments"."resource_type" = ? AND "active_admin_comments"."resource_id" = ? AND "active_admin_comments"."namespace" = ? LIMIT ? OFFSET ? [["resource_type", "Field"], ["resource_id", 22], ["namespace", "admin"], ["LIMIT", 1], ["OFFSET", 0]]
↳ /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Rendered /home/divepit/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.3.1/app/views/active_admin/resource/show.html.arb (73.4ms)
Completed 200 OK in 77ms (Views: 74.9ms | ActiveRecord: 0.5ms)
Thanks in advance for any tips on how to solve this! :)
First things first. Your associations are correct, but as per your associations you should not have field_id in semester table. Instead you should have semester_id in fields table. Also you should change semesters_attributes to semester_attributes
Unpermitted parameter: :semester_id
You should permit semester_id in the fields_attributes
fields_attributes: [ :semester_id, :name]
And finally in Rails 5, whenever a belongs_to association is defined, it is required to have the associated record present by default. To avoid this default behavior, you need to add optional: true

rails validates_format_of with IP addresses

I'm allowing a user to enter IP addresses in an input field which may be of different types delimited by a comma, such as (in no particular order):
192.168.1.1,192.168.2.1-25,10.10.10.0/24,192.168.1.2
This 'string' would get saved in my DB under device.ips_to_scan.
I want to validates_format_of on these, but am finding it a little difficult to write a regex that seems to work in rails, while it does work on regex101 (https://regex101.com/r/nf2bnM/1):
validates_format_of :ips_scan, with: /\A([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?,?\Z/i, on: :update
This one is expected to fail:
Started PUT "/devices/2" for 127.0.0.1 at 2018-02-19 22:03:15 -0500
Processing by DevicesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"EQCFG6/xoJHtP6Nd3oqaYRW6mypfEoCMrnio1yj6loP+KtvjgLZ9Gmhb0oTwCjD0RGH+qQuctZFVIvF5HBJcGw==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,a.b.c.d", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Device Load (1.6ms) SELECT "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.5ms) BEGIN
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.5ms) ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 47ms (ActiveRecord: 12.1ms)
...But this one should have worked:
Processing by DevicesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JJfmT/0l5MEDc+gUH/WHHp3bbgyzjGa0xTzaXM3E/WHLvbi30mI5SoYXmc0xdS2LzAALj+cCU6k+ZoPy+Sw3+Q==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,192.168.1.2", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Device Load (0.7ms) SELECT "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.6ms) BEGIN
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.6ms) ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 17ms (ActiveRecord: 3.5ms)
Last thing I can think of, is that I do have strong parameters, but I'm permitting ips_scan, so that this shouldn't be an issue:
def update
if #device.update(device_params)
flash[:notice] = 'Successful update'
respond_with :edit, :device
else
flash[:warning] = 'Unable to update'
respond_with :edit, :device
end
end
private def device_params
params.require(:device).permit(:token, :ips_scan, :ips_exclude)
end
I'm hoping you rubyist's out there have a eloquent solution. The first thought that comes to mind is that I have to split the string, and check each element sequentially to ensure it matches instead.
While I'm still open to a nice eloquent one-liner within the Model itself, I was able to get this working through creating a concern:
models/concerns/ip_validator.rb
class IpValidator < ActiveModel::Validator
def validate(record)
ips = record.ips_scan.split(',')
ips.each do |ip|
/([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?/ =~ ip
record.errors.add(:ips_scan, ' is not valid') unless $LAST_MATCH_INFO
end
end
end
The call in my model now looks like:
validates :ips_scan, :ips_exclude, ip: true, on: :update
You can use this method in your custom validator to check an IP address
require 'ipaddr'
def valid_ip_addr?(ip_addr)
IPAddr.new(ip_addr)
true
rescue IPAddr::InvalidAddressError => _error
false
end

Rails multi-level relation destroyed when updating

I have a four-level deep model structure: Domain > Subject > Device > Property
class Domain < ApplicationRecord
has_many :subjects
end
class Subject < ApplicationRecord
has_many :devices
belongs_to: :domain
end
class Device < ApplicationRecord
has_many :properties
belongs_to: :subject
end
class Property < ApplicationRecord
belongs_to :device
end
controller code
def update
result = #subject.update(parameters)
if result
render json: #subject
else
render_errors(#subject.errors)
end
end
#subject is retrieved as a before action, by querying the model tree from domain onwards, using domain_id and id parameters for domain and subject respectively. parameters is simply a hash of parameters, e.g. {name: :new_name}
When updating a Subject, the relation to domain is lost, i.e. domain_id is set to NUL by rails. The entire model tree below subject will also be disconnected from the parent domain as a result.
When removing has_many: :devices from the Subject model, everything works as expected. I just want to update a subject and preserve the relation to the parent domain. How would I achieve this with the model described above?
EDIT 1 - Added log of both situations.
Log with full relational model (that results in the bug)
Domain Load (0.5ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s1"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
(0.0ms) begin transaction
Domain Load (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
SQL (7.1ms) UPDATE "subjects" SET "domain_id" = NULL WHERE "subjects"."domain_id" = ? AND "subjects"."id" = 5 [["domain_id", 3]]
Subject Exists (0.0ms) SELECT 1 AS one FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s2"], ["LIMIT", 1]]
SQL (1.0ms) UPDATE "subjects" SET "name" = ?, "updated_at" = ? WHERE "subjects"."id" = ? [["name", "s2"], ["updated_at", "2017-07-31 08:46:38.171240"], ["id", 5]]
(7.5ms) commit transaction
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects"
Completed 200 OK in 30ms (Views: 0.5ms | ActiveRecord: 16.6ms)
Log when removing belongs_to: :devices from Subject model
Domain Load (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s3"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.0ms) begin transaction
Domain Load (0.5ms) SELECT "domains".* FROM "domains" WHERE "domains"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
SQL (6.5ms) UPDATE "subjects" SET "name" = ?, "updated_at" = ? WHERE "subjects"."id" = ? [["name", "s4"], ["updated_at", "2017-07-31 08:48:57.962218"], ["id", 6]]
(7.0ms) commit transaction
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects"
Completed 200 OK in 25ms (Views: 0.4ms | ActiveRecord: 15.1ms)
Edit 2 - There might be something wrong with the seed data...
domainA = Domain.create(name: :company)
s1 = domainA.subjects.create(name: :subject)
# domainA.save
d1 = s1.devices.create(name: :device)
# s1.save
p1 = d1.properties.create(name: :prop1, property_type: :double, value: 10.0)
p2 = d1.properties.create(name: :prop2, property_type: :string, value: :on)
p3 = d1.properties.create(name: :prop3, property_type: :string, value: :Lamp)
# d1.save
domainA.save
When removing has_many: :devices from the Subject model, everything
works as expected.
Going on with your models, Your Device model is flawed. You have belongs_to :device inside Device. Perhaps you should have belongs_to :subject as per the associations. This could have lead to your current problem. Try changing the Device model like so
class Device < ApplicationRecord
has_many :properties
belongs_to: :subject
end

Resources