I've just upgraded from Rails 5.0.0 to 5.1.1 and started getting a ton of deprecation warnings like this:
DEPRECATION WARNING: The behavior of changed_attributes inside of
after callbacks will be changing in the next version of Rails. The new
return value will reflect the behavior of calling the method after
save returned (e.g. the opposite of what it returns now). To
maintain the current behavior, use
saved_changes.transform_values(&:first) instead.
and this:
DEPRECATION WARNING: The behavior of attribute_changed? inside of
after callbacks will be changing in the next version of Rails. The new
return value will reflect the behavior of calling the method after
save returned (e.g. the opposite of what it returns now). To
maintain the current behavior, use saved_change_to_attribute?
instead.
I don't use those methods explicitely anywhere in my project and the warnings are pointing mostly at create and update calls on my models.
I believe it has something to do with my validations and after_update and after_create callbacks where I use confitions like if: { author_id_changed? } but I have no idea what to do with them.
I also believe the warning is related to this massive update to ActiveRecord.
Would appreciate any hand you can give with this.
UPD
This article helped alot!
Well, got around everything by running bundle update and updating the gems and also following this article and changing attribute_changed? calls in after_ callbacks (but not in before_ callbacks and validations) and switching from attribute_was to attribute_before_last_save.
For After callbacks, you can use saved_change_to_attribute?
For Before callbacks and validations, you can use will_save_change_to_attribute?
I hope this information will help!
I've done an upgrade to Rails 5.1.6 and have the same DEPRECATION warnings. If ever anyone still wants to solve this warning. Here are the steps I took:
Search all of your*_changed?
Changed this:
if name_changed?
...
if user_id_changed?
To this if it is inside after_* (after_save, after_commit, after_update, etc.) blocks:
if saved_change_to_name?
...
if saved_change_to_user_id?
AND to this if it is inside before_* (before_save, before_commit, before_update, etc.) blocks:
if will_save_change_to_name?
...
if will_save_change_to_user_id?
On my own opinion, this is quite tricky thing to change since we've been used to attribute_changed?. But change is good. The syntax also made more sense now.
You can change author_id_changed? to saved_change_to_author_id?
Related
In a rails app using sorbet, when you have a method that expects an instance of a type, say Foo. And you need to initialize an instance of Foo in an initializer for the app that persists in memory between requests. And then you make any change to the source code of Foo or any file that Foo uses. Then sorbet believes that this instance is no longer an instance of Foo, and you need to restart your app in order for it to stop erroring.
This seems like a rare case, but in our app, we use a bit of dependency injection, and it's a large team. So sorbet is making us restart our app almost every time we do a git update, and many, many times throughout the day as we write code. We have a large app which takes a while to restart, and it's quite frustrating to have to do this.
Any ideas on how to fix this? If helpful, I could make a sample rails app to demonstrate this behavior.
I ran into a similar issue myself when I was referencing a model in a Rails initializer. I was advised that there are 2 solutions:
Refactor the code so that you don't have this issue
Add checked(:tests) or checked(:never) to the sigs of methods that are having this problem. This would preserve the runtime and test time checks, but eliminate the errors in development. See the link below for documentation
https://sorbet.org/docs/runtime#checked-whether-to-check-in-the-first-place
What is a "suitable guard" and what does it look like?
Linked this question because it refers to the same compiler message and the answer mentions a guard but not how to create one. Looked through the AspectJ docs but did not find and answer there.
This Lint warning is usually switched off in AJDT (AspectJ Development Tools) within Eclipse, but you can activate it as a warning or even error like this (I had to do it to actually see it at all when trying to reproduce your issue):
You can just ignore the Lint warning because basically it only says that there is no way for certain pointcuts to populate the thisJoinPoint object lazily during runtime because the pointcut has no dynamic component like if(), cflow() or similar, which is actually good news because it means that all your joinpoints can be determined statically during compile/weave time and are thus faster than dynamic pointcuts. On the other hand, the warning says that the tjp object always has to be created because for some reason it is also always needed during runtime and thus cannot be instantiated lazily.
It appears there's not much documentation on Rails template handlers. There's the included handlers, like RJS, ERB, and Builder, which offer some assistance.
I'm trying to implement my own, and I've succeeded, albeit with a bit of weird code, or possibly there's something I don't quite understand.
class MyHandler < ActionView::Template::Handler
def call(template)
template.source.inspect
end
end
So what's weird is that I have to call inspect, otherwise Rails will try to eval the string as Ruby code.
I was under the impression that that's what include ActionView::...::Compilable did (which I'm not including in my code).
Now, if I make my template "compilable" (by using the include... statement), it still looks for the call method instead of the compile method.
So could anyone explain to me a bit more about how this works?
Thanks!
I've just been going through this problem myself. Basically rails expects the renderer's .call method to return ruby code that will render your template. It then dynamically generates a method which runs this code, and injects it into a module.
The module has all of the url/application helpers included, which means they're in scope for the template.
So, in answer to your question the solution is for .call to return some ruby code that outputs your rendered template as a string, or for it to render ruby code that invokes your template engine.
Check out tilt and temple, I have learnt a lot about template engines reading their code.
I'm trying to extract some common code into a gem.
I'm thinking that acts_as_somethingis a good strategy for simple re-use.
Is there a good tutorial that discusses this for rails3 gems? I've found several that discuss rails2 (such as http://guides.rubyonrails.org/plugins.html) but that is specific to rails2
here are some of the tutorials/blogs I've already read:
http://guides.rubyonrails.org/plugins.html
http://www.thoughtsincomputation.com/categories/coding-gems-for-rails-3-tutorials (this is an excellent start but doesn't cover the act_as_* issue or controllers)
thanks
UPDATE: I've added a blog post based on this answer, but with much more detail: http://thoughtsincomputation.com/posts/coding-an-acts_as-gem-for-rails-3
--
I'm not aware of another tutorial source off the top of my head, but here are some general tips.
Rails 3 makes use of a really useful feature called Railtie - see http://api.rubyonrails.org/classes/Rails/Railtie.html .
So, if I were implementing an acts_as_* gem, I'd start there. My railtie might look something like:
# lib/acts_as_awesome/railtie.rb
require 'rails'
require 'acts_as_awesome'
module ActsAsAwesome
class Railtie < Rails::Railtie
config.to_prepare do
ApplicationController.send(:extend, ActsAsAwesome::Hook)
end
end
end
and the ActsAsAwesome::Hook code:
# lib/acts_as_awesome/hook.rb
module ActsAsAwesome::Hook
def acts_as_awesome(*args)
options = args.extract_options!
# do the things that make the controller awesome.
include ActsAsAwesome::InstanceMethods
before_filter :an_awesome_filter
end
end
I feel the concepts here are sound and have used similar processes before. Basically, it would tell Rails to execute the to_prepare block once during production and before each request in development (we want that because ApplicationController will be reloaded at those times, potentially wiping out our hook method); and the hook is just that: it adds a hook to all controllers (or rather, all controllers that extend ApplicationController) to allow the user to introduce the real "Awesome" code into their controllers without otherwise affecting controllers that don't need it.
The #acts_as_awesome hook doesn't, in itself, convey the Awesome functionality. That's because not all controllers might need this functionality. Instead, the method is responsible for introducing the real awesome stuff, via the ActsAsAwesome::InstanceMethods module. This way, the user only gets the Awesome functionality if they explicitly call the acts_as_awesome method. It also adds a before filter to the controller to demonstrate that the code in this method would be evaluated exactly the same as if it were in the target controller class itself.
This technique should work exactly the same if you're targeting models instead of controllers: just inject your hook into ActiveRecord::Base. As AR:B is only loaded at Rails boot, you should probably be able to put that into an initializer (refer to the Railtie docs), but I reserve the right to be mistaken here.
A gotcha concerning the railtie: the documentation reads as if it should have been autodetected, but I often have problems with this. To get around it, simply require the railtie from your gem's main source file (in the above example, that would be lib/acts_as_awesome.rb).
You can see the entire ActsAsAwesome source in all its glory at my github account:
http://github.com/sinisterchipmunk/acts_as_awesome
I hope this is helpful. Your question was somewhat high-level so a high-level response is the best I can do.
-Colin MacKenzie IV
http://thoughtsincomputation.com
#sinisterchipmnk
When I'm using VB.NET to use subsonic, It seems to have problem marking records as Old and Clean. Whenever I query using ExecuteSingle or ExecuteTypedList, i need to manually MarkClean and MarkOld, else whenever I save it will save as a new record.
Am I the only one facing this problem ? I'm using SubSonic 2.2 btw.
I checked the source code of SubSonic.. and I found that the VB class generator doesn't implements the IActiveRecord. I think most likely is because VB.Net doesn't seem to support 're-implementation' of inheritance or whatever you call that...
So when I debug, I found that Utility.IsSubSonicType returns false (because the ActiveRecord class returns as IReadOnlyRecord, but IsSubSonicType checks for IActiveRecord and IRecordBase) and thus doesn't call the SetLoadState and MarkClean.
So I'm not sure if this is a bug or it is intentional. Any way to solve this?
When you use ExecuteSingle or ExecuteTypedList, you could be doing with a class that didnt have those properties, I think the intention is that you are populating a POCO and not (necessarily) an Entity or other ORM object.
ExecuteAsCollection and all of the .Load methods behave as you expect because they call SetLoadState() and/or MarkClean().
Personally, I dont face this problem because I use Subsonic purely as a (smart) DAL (CRUD Only) and my own entity layer takes care of things like dirty/new.
Yes, I had the same problem. MarkClean and MarkOld before setting properties and saving fixed the issue. see this