A plugin provides a model called User. Is it possible to reopen it in my app?
If I create app/models/user.rb and try it there, the whole model is overridden and the original plugin methods are no longer available.
This is the only way I found so far:
# app/models/plugin_user.rb
class PluginUser
def self.load
User.class_eval do
# my code here
end
end
end
# plugin model:
class User
# ...
end
PluginUser.load
It would be nice if there was a way of doing this without modifying the plugin code. In this case it doesn't matter because the plugin is mine, but if I needed to do the same to another plugin I'd need to fork it.
Related
I have written a module - JsonLog - that uses the append_info_to_payload hook provided by Rails to add some custom metrics to the logging instrumentation. I want to include/mixin this module in all the controllers of ActiveAdmin.
I've tried the following, and it works...
ActiveAdmin.register MyModel do
controller do
include JsonLog
end
end
...but this will force to me write the boilerplate code in every single model/controller that I'm registered with ActiveAdmin. How do I do this in one place (and in the process also ensure that this boilerplate is never missed out)?
Don't be shy to read the source. There is an ActiveAdmin::BaseController that inherits from InheritedResources::Base that in turn inherits from your ApplicationController. If you really need to specifically modify ActiveAdmin::BaseController then try this in config/initializers/active_admin.rb:
ActiveAdmin::BaseController.class_eval do
include JsonLog
end
I'm using PaperTrail 4.1 with Rails 4.2.
I have defined several custom methods in an initializer (see: How to add a method to the versions model of Paper_trail?)
#config/initializers/paper_trail.rb
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version < ActiveRecord::Base
scope :scoped, lambda { #selects some records }
def custom_method
#does some stuff
end
end
end
Every so often in development environment I get a method not defined error for methods/ scopes defined in this initializer.
Restarting the server fixes the problem.
Why are these methods being 'lost' to Rails?
Is this an issue that will also present itself in production or other environments?
What steps can I take to find the cause of this issue?
For anyone else arriving here, apparently this is a known issue with PaperTrail
From https://github.com/airblade/paper_trail/pull/492
Now the paper_trail source get's reloaded in the development
environment when saving a file which means the class gets discarded
from the cache and rebuild from the paper_trail sources. The
initializer is not interpreted again since they are one time only, no
module_eval, no abstract class -> exceptions.
And a fix has been included in the latest version of the gem: https://github.com/airblade/paper_trail/pull/557
In essence, it is no longer advised to use an initializer to add custom methods to PaperTrail, and instead to use a model that inherits from PaperTrail (which is a much better fit with AR).
# app/models/paper_trail/version.rb
module PaperTrail
class Version < ActiveRecord::Base
include PaperTrail::VersionConcern
# my custom methods
end
end
I'm a newbie to rails. I have created a reports module for a particular project. Now, we want to make it generic across all project like a reports gem. My question is not about how to create & use gem. My questions is "how to make a generic reports lib". For eg. I have a helper module in reports,
module Libquery
module Helper
include QueryConstants(which is dynamic - based on the project)
#methods
end
end
end
My approach: each project will include LibQuery::Helper and also it will include its own constants file.
module ProjectX
module Query
module Helper
include Libquery::Helper
#nothing - inherit all helper methods in libquery
end
end
end
But I'm wondering if that's the most elegant way of doing things ? Or any better way to do it?
First of all, all modules must be capitalized:
module MyModuleName
Second, to use a lib it's best to include it in autoload_paths (in your application.rb file) like this
config.autoload_paths += %W(#{Rails.root}/lib/my_shared_libs)
This means rails will load it automatically, and you'll have available 'out of the box'.
Third, external modules shouldn't depend on project-based modules and classes, since the whole point is to make them easily reusable.
So it boils down to this:
#/lib/my_shared_libs/fun_things.rb
module FunThings
... your code
def laugh
puts 'haha'
end
end
#/models/user.rb
class User
include FunThings
end
User.new.laugh # => 'haha'
Im having problem to use table_name_prefix on my projects. I have a main apllication in my project that have others applications as plugins, these plugins works like a subsystem from the main application.
To organize the tables on database of the subsystems I would like to use the table_name_prefix of the ActiveRecord Plugin.
If I put on init.rb of plugin the command config.active_record.table_name_prefix = "per_" the main application will not work because the ActiveRecord will try to find for "per_users" but the only thing I want is that only the Plugin on my main application use the prefix "per_".
I tried to create a rails folder at my plugin with the command above but the same problem occurs, all the application try to find for prefixed table name.
An alternative is use the set_table_name in the model of plugin, but its not good for me because Im developing subsystems as rails plugin and I dont want to change the models when put the subsystem at the main application.
Someone can help me?
To have each plugin with own prefix, for Rails 3, try organize Your models inside plugin in namespace:
class Foo::Bar < ActiveRecord::Base
...
end
module Foo
def self.table_name_prefix
'foo_'
end
end
This will works just inside the plugin without changing anything inside the main application.
Other approach is to use some main model and inherit it from others like that:
class Foo < ActiveRecord::Base
def self.table_name_prefix
'foo_'
end
end
class Bar < Foo
...
end
sometimes this approach is used to extend all models with extra features.
More information in Rails documentation here
Take a look at this question. I ran into the same problem, forgot my application's name (main module) had the same name as a namespace for my models.
I am having a validation problem when writing a Redmine plugin.
I'm writing a hook for the issue model, and as a part of the hook method, i would like to invalidate the creation of the issue, by adding a custom error:
def controller_issues_new_before_save( context = { } )
context[:issue].errors.add(:due_date, "A custom error")
end
For testing purposes, I have written a patch that overwrites Issue.validate_on_create, but it seems that every time when entering validate_on_create errors.count is set to zero.
I need to stop the creation of the issue object , but only when an attribute is set into another model object.
I thought about writing this in the validate_on_create method, but then I would need to pass it the other object.
The first solution that I thought about would be to insert an additional field in the Issue model, and modify it inside the hook.
Something like :
def controller_issues_new_before_save( context = { } )
context[:issue].can_validate = false
end
def validate_on_create
unless can_validate
errors.add("error", "A custom error")
end
end
where Issue.can_validate is an addition to the Issue model
However, this does not seem the best approach here. Is there an easier way?
If you are wanting to validate data you should patch the models directly and not use hooks. Hooks are meant to be used to insert HTML onto a page or change the control flow of a controller. Using hooks also means that your code will only work for that one path through the application, so if someone creates an issue somewhere else then you code will not run.
To create a patch you just need to do two things:
Create a module that has your code
Make Redmine include that module in it's Issue class
I've done this exact thing in a plugin that adds a validation on an Issue to require that due dates are set in the future. The patch for it is rather simple so I'll include it here:
module RedmineRequireIssueDueDateInFuture
module Patches
module IssuePatch
def self.included(base)
base.class_eval do
unloadable
validate :due_date_in_future
protected
def due_date_in_future
return true if due_date.nil?
if due_date.to_time < Date.today.beginning_of_day
errors.add :due_date, :not_in_future
end
end
end
end
end
end
end
Inside of the class_eval is where you would put your own code, I'd recommend using a different name than validate_on_create. Otherwise you might have problems with other code if they want to use that method too.
The second part (including the module into Redmine) is rather easy. Just require the Issue class and use include to add it to the class.
# init.rb
require 'dispatcher'
Dispatcher.to_prepare :redmine_require_issue_due_date_in_future do
require_dependency 'issue'
Issue.send(:include, RedmineRequireIssueDueDateInFuture::Patches::IssuePatch)
end
You need to wrap this in the Dispatcher to keep things working in development mode. I've written about it on my blog.
Feel free to copy my plugin from github to make your changes, it's pretty simple. https://github.com/edavis10/redmine_require_issue_due_date_in_future
Since Redmine 2.0, you should replace the code in init.rb by this in Eric Davis response :
#init.rb
ActionDispatch::Callbacks.to_prepare do
require_dependency 'issue'
Issue.send(:include, RedmineRequireIssueDueDateInFuture::Patches::IssuePatch)
end