I am using https://github.com/mailboxer/mailboxer and I am trying to add my own photos to attach to each message
inside /config/initializes/extensions/mailboxer/message.rb
Mailboxer::Message.class_eval do
has_many :photos, as: :imageable, :class_name => 'Photo'
end
This works great, except that it randomly throws an error:
undefined method `photos' for #<Mailboxer::Message:0x6eb0758>
When I start up the server for the first time it works. When I start modifying code (can be anything, nothing related to mailboxer) I get the error. I have to restart the server to get it working again.
I tried putting the file outside the initializes folder and adding an include path as the last line in config/boot.rb, same issue.
Any ideas as to why it is losing reference?
When Rails detects your code has been modified, it "forgets" all the models, etc. it has auto-loaded, including Mailboxer::Message. The next time that model is used, it is reloaded from the mailboxer gem without your monkey patches.
To ensure your monkey patches "stick", I think you need to give Rails a hint that you want your code reapplied when it does a reload. Putting your patches in a to_prepare block may do the trick:
Rails.application.config.to_prepare do
Mailboxer::Message.class_eval do
has_many :photos, as: :imageable, :class_name => 'Photo'
end
end
Related
After moving my application from Rails 4.2.8 to 5.2.3 the inserts fail with
Billings event must exist
The application receives a single cascaded hash with one event and many associated billings and should put this into the database in one single transaction; this did always work before.
class Event < ActiveRecord::Base
has_many :billings, -> { where('1 = 1') }, dependent: :destroy
accepts_nested_attributes_for :billings
validates_associated :billings
end
class Billing < ActiveRecord::Base
belongs_to :event
validates_presence_of :event_id, on: :update
end
class EventsController < ApplicationController
def kC
#event = Event.new(event_params)
if #event.save
[doesn't get here anymore]
end
end
end
There is no controller for billings, they do only exist via their associated event.
Quick analyses finds in the docs mention that
belongs_to :event, optional: true
would avoid this error, and it indeed does. But this seems very wrong to me, because in this application billings must never exist without their event, it is NOT optional!
But then, what is the correct solution?
Further analysis show: all validations get processed, but a before_create() callback is never reached. The "must exist" error is added at some internal place, it does not come from my code.
Furthermore, when creating a template with only the code as shown above, I found the problematic code to be the scoping -> { where('1 = 1') }
In the real application this is a more complex (and more useful) term, but this simple and seemingly transparent term triggers the problem just the same.
There are many similar questions here, but then, many have a situation where the association is indeed optional, some have nonstandard namings (I don't think I have, as it worked before), and I didn't find one for this case where the belonging model is fully handled via the having one.
In Rails 5, whenever we define a belongs_to association, it is required to have the associated record present by default. It triggers validation error if the associated record is not present. To remove this default behavior, we can use new_framework_defaults.rb initializer which comes with Rails 5.
(For more info you can check this https://github.com/rails/rails/pull/18937)
When upgrading from the older version of Rails to Rails 5, we can add this initializer by running bin/rails app:update task.
This newly added initializer has the following config flag that handles the default behavior
Rails.application.config.active_record.belongs_to_required_by_default = true
We can turn off this behavior by setting its value to false
Rails.application.config.active_record.belongs_to_required_by_default = false
I found what appears to be the correct solution:
class Event < ActiveRecord::Base
has_many :billings, -> { where('1 = 1') }, dependent: :destroy, inverse_of: :event
accepts_nested_attributes_for :billings
validates_associated :billings
end
Adding this inverse_of: option in this way resolves the problem.
Preliminary tentative root cause analysis:
The (sparse) documentation for the inverse_of option does suggest to add it to the belongs_to feature; it does not mention adding it to has_many (it does not discourage that either). Adding it to belongs_to does not improve things in this case, also the use-case in the documentation does not apply here.
Nevertheless, the documentation mentions an "automatic guessing" of associations, and that this automatic guessing would be omitted in certain cases as declared in AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS.
Searching for this term in the source leads to a private method can_find_inverse_of_automatically?(), where it becomes obvious that also a scope will result in the automatic guessing being omitted.
It appears that the unravelling of a cumulative insert in some way needs to pinpoint the "inverse_of" (be it automatically or coded), or otherwise it would consider the owning relation as nonexisting - with the latter, due to the mentioned change in Rails 5, now leading to a validation error.
I have a rails app that is using Redis as the DB, and Ohm as an ORM.
I have a model Activity that has a set of Users. In the rails console I am able to call Activity[id].users.to_a and get the expected list. When trying to do the same thing in a method in my app, I always get an empty string. I've tried this several different ways, including using self.users.to_a in the method and nothing has worked.
I haven't come across a problem like this before where everything is working fine in console but not in the app. BTW the rails.env is set to development.
Any help would be really appreciated, this has stumped me for a bit now. Thanks.
Activity.rb
class Activity < Ohm::Model
include Ohm::Timestamps
include Ohm::DataTypes
include Ohm::Callbacks
include ModuleLibrary
attribute :type
index :type
attachment :attached
attribute :time, Type::Integer
attribute :description
attribute :to_calendar, Type::Integer
set :contacts, :Contact
set :users, :User
reference :company, :Company
reference :account, :Account
def user_names #empty string in app, works fine in console
users.map(&:name).to_sentence
end
end
EDIT: So apparently it turns out where I was calling the method from, the users hadn't been added yet. (duh!) I moved where the method is called to a different spot, and it's all working now.
I have a Game model which has_many :texts. The problem is that I have to order the texts differently depending on which game they belong to (yes, ugly, but it's legacy data). I created a Text.in_game_order_query(game) method, which returns the appropriate ordering.
My favourite solution would have been to place a default scope in the Text model, but that would require knowing which game they're part of. I also don't want to create separate classes for the texts for each game - there are many games, with more coming up, and all the newer ones will use the same ordering. So I had another idea: ordering texts in the has_many, when I do know which game they're part of:
has_many :texts, :order => Text.in_game_order_query(self)
However, self is the class here, so that doesn't work.
Is there really no other solution except calling #game.texts.in_game_order(#game) every single time??
I had a very similar problem recently and I was convinced that it wasn't possible in Rails but that I learned something very interesting.
You can declare a parameter for a scope and then not pass it in and it will pass in the parent object by default!
So, you can just do:
class Game < ActiveRecord
has_many :texts, -> (game) { Text.in_game_order_query(game) }
Believe or not, you don't have to pass in the game. Rails will do it magically for you. You can simply do:
game.texts
There is one caveat, though. This will not work presently in Rails if you have preloading enabled. If you do, you may get this warning:
DEPRECATION WARNING: The association scope 'texts' is instance dependent (the scope block takes an argument). Preloading happens before the individual instances are created. This means that there is no instance being passed to the association scope. This will most likely result in broken or incorrect behavior. Joining, Preloading and eager loading of these associations is deprecated and will be removed in the future.
Following up using PradeepKumar's idea, I found the following solution to work
Assuming a class Block which has an attribute block_type, and a container class (say Page), you could have something like this:
class Page
...
has_many :blocks do
def ordered_by_type
# self is the array of blocks
self.sort_by(&:block_type)
end
end
...
end
Then when you call
page.blocks.ordered_by_type
you get what you want - defined by a Proc.
Obviously, the Proc could be much more complex and is not working in the SQL call but after there result set has been compiled.
UPDATE:
I re-read this post and my answer after a bunch of time, and I wonder if you could do something as simple as another method which you basically suggested yourself in the post.
What if you added a method to Game called ordered_texts
def ordered_texts
texts.in_game_order(self)
end
Does that solve the issue? Or does this method need to be chainable with other Game relation methods?
Would an Association extension be a possibility?
It seems that you could make this work:
module Legacy
def legacy_game_order
order(proxy_association.owner.custom_texts_order)
end
end
class Game << ActiveRecord::Base
includes Legacy
has_many :texts, :extend => Legacy
def custom_texts_order
# your custom query logic goes here
end
end
That way, given a game instance, you should be able to access instance's custom query without having to pass in self:
g = Game.find(123)
g.texts.legacy_game_order
Here is a way where you can do it,
has_many :texts, :order => lambda { Text.in_game_order_query(self) }
This is another way which I usually wont recommend(but will work),
has_many :texts do
def game_order(game)
find(:all, :order => Text.in_game_order_query(game))
end
end
and you can call them by,
game.texts.game_order(game)
Im not sure what your order/query looks like in the in_game_order_query class method but i believe you can do this
has_many :texts, :finder_sql => proc{Text.in_game_order_query(self)}
Just letting you know that I have never used this before but I would appreciate it if you let me know if this works for you or not.
Check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many for more documentation on :finder_sql
I think if you want runtime information processed you should get this done with:
has_many :texts, :order => proc{ {Text.in_game_order_query(self)} }
I am trying to associate a set of uploaded pictures with a post. In my controller I am using the following code:
image_uploads = params[:image_uploads]
iu = ImageUpload.find(image_uploads)
#post.image_uploads = iu
While this does make the uploaded images accessible from #post.image_uploads, I think it doesn't associate these images with the post because when the post is deleted, the image uploads are not deleted -- even though I have used :dependent=>:destroy for their relationships.
> Post.first.delete
=>[]
> ImageUpload.all
=> [#<ImageUpload id: 3 ...>]
And this is the model:
class Post < ActiveRecord::Base
has_many :image_uploads, :dependent => :destroy
end
class ImageUploads < ActiveRecord::Base
belongs_to :post
end
What can I do to make sure that cascading deletes work?
Try instead:
Post.first.destroy
destroy invokes the callbacks defined, whereas delete deletes the record directly using SQL without creating an AR object.
What you do seems correct to me. You have a post with image uploads dependent on the post and you should get them deleted when a post is deleted. I could be wrong but i do not see anything.
However, i see something that could cause heavy problems and it could also be the source of your problem(it's a source of many).
You have a model that ends with an S. Not a good idea. Do yourself a favor and change to ImageUpload please :)
I am using Ruby on Rails to create a website for a game I play.
I have a User model and a Starbase model. The relationship I am trying to setup is like so
class User < ActiveRecord::Base
has_many :starbases
end
class Starbase < ActiveRecord::Base
belongs_to :user
end
However when I open script/console and try to access the users starbases it gives me an error: NameError: uninitialized constant User::Starbasis.
It seems as if it is a problem with inflection and rails is not pluralizing starbase correct.
I have tried adding this to the inflections.rb in the intializers folder:
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural 'starbase', 'starbases'
end
but it still does not solve the issue. Could anyone give advice on how to get this working?
Have you tried adding a line for the inverse inflection (i.e. 'singular'):
inflect.singular "starbases", "starbase"
I tried your example in my console and it was the singularization that caused problems, not the other way around. I'm not sure if this fixes other issues (like routes), but it should fix the simple stuff (I think).
Little trick i picked up to double check how Active Support might singularize, or pluralize my Class names, and/or Module names.
have your rails app server running and in a new tab enter into your rails console by typing rails console. In there you can easily double check for the correct style for your names.
long way ActiveSupport::Inflector.pluralize "fish"
# => "fish"
short way "fish".pluralize
# => "fish"
You can find more examples here
https://github.com/rails/rails/blob/master/activesupport/test/inflector_test_cases.rb