How do I find a specific delayed job (not by id)? - ruby-on-rails

Delayed::Job serializes your class, method & parameters into the handler field. We currently resort to hardcoding this serialized method into our code. This is gross.
How should we be constructing the handler so we can look up an existing queued up job?

This what I do:
1) Add two new columns to delayed_jobs table
db/migrations/20110906004963_add_owner_to_delayed_jobs.rb
class AddOwnerToDelayedJobs < ActiveRecord::Migration
def self.up
add_column :delayed_jobs, :owner_type, :string
add_column :delayed_jobs, :owner_id, :integer
add_index :delayed_jobs, [:owner_type, :owner_id]
end
def self.down
remove_column :delayed_jobs, :owner_type
remove_column :delayed_jobs, :owner_id
end
end
2) Add a polymorphic association to Delayed::Job model
config/initializers/delayed_job.rb
class Delayed::Job < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
attr_accessible :owner, :owner_type, :owner_id
end
3) Monkey patch ActiveRecord::Base to contain a jobs association
config/initializers/active_record.rb
class ActiveRecord::Base
has_many :jobs, :class_name => "Delayed::Job", :as => :owner
def send_at(time, method, *args)
Delayed::Job.transaction do
job = Delayed::Job.enqueue(Delayed::PerformableMethod.new(self,
method.to_sym, args), 10, time)
job.owner = self
job.save
end
end
def self.jobs
# to address the STI scenario we use base_class.name.
# downside: you have to write extra code to filter STI class specific instance.
Delayed::Job.find_all_by_owner_type(self.base_class.name)
end
end
4) Triggering a jobs
class Order < ActiveRecord::Base
after_create :set_reminders
def set_reminders
send_at(2.days.from_now, :send_email_reminder)
end
def send_email_reminder
end
# setting owner for handle_asynchronously notation.
def foo
end
handle_asynchronously :foo, :owner => Proc.new { |o| o }
end
5) Inspecting jobs
Order.jobs # lists all the running jobs for Order class
order1.jobs # lists all the running jobs for Order object order1

Related

Active Admin Name Error uninitializedconstant Resource::Users

So I have a couple of models in my app and they are all registered with ActiveAdmin. They all work great except for one and I can't figure out why. I keep getting the same error:
NameError at /admin/reports
uninitialized constant Report::Users
The model that it is happening on is called Report
class Report < ActiveRecord::Base
belongs_to :users
belongs_to :cars
enum reason: [:accident,:totaled,:stolen]
validates :reason, presence:true
end
The controller looks like this:
Class ReportsController < ApplicationController
before_action :authenticate_user!
def create
#car=Car.find(params[:car_id])
#report=#car.reports.build(report_params)
#report.user_id=current_user.id
#report.car_id=#car.id
if #report.save
redirect_to car_path(car)
else
render 'new'
end
end
def destroy
#report=Report.find(params[:id])
#report.destroy
end
private
def report_params
params.require(:report).permit(:reason)
end
end
This is the migration used to create the model:
class CreateReports < ActiveRecord::Migration
def change
create_table :reports do |t|
t.references :user, index: true
t.references :car, index: true
t.integer :reason, default: 0
t.timestamps null: false
end
add_foreign_key :reports, :users
add_foreign_key :reports, :cars
end
end
Lastly here is the active_admin app/admin/report.rb:
ActiveAdmin.register Report do
# See permitted parameters documentation:
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
#
# permit_params :list, :of, :attributes, :on, :model
#
# or
#
# permit_params do
# permitted = [:permitted, :attributes]
# permitted << :other if resource.something?
# permitted
# end
end
I have been trying to figure it out for a couple of hours. Solutions that I saw on SO that don't work. I ran rails generate active_admin:resource Report to create it so it is singular. Why is it misbehaving?
NameError at /admin/reports uninitialized constant Report::Users
Association name for a belongs_to should be singular as per naming conventions.
class Report < ActiveRecord::Base
belongs_to :user #here
belongs_to :car #and here too
enum reason: [:accident,:totaled,:stolen]
validates :reason, presence:true
end

Rails polymorphic # mentions create action

I'm trying to create a simple # mentions model similar to twitters for my app. I've started building it, but I don't know how I would handle the actual creation of the mention. I need some way to scan let's say a status before it's created for any # symbols, then checking the text following against the database for any matching usernames. If there's a match then a mention gets created along with the status. Can someone point me in the right direction?
Here's what I have so far:
db/migrate/create_mentions.rb
class CreateMentions < ActiveRecord::Migration
def change
create_table :mentions do |t|
t.belongs_to :mentionable, polymorphic: true
t.timestamps
end
add_index :mentions, [:mentionable_id, :mentionable_type]
end
end
models/mention.rb
class Mention < ActiveRecord::Base
belongs_to :mentionable, polymorphic: true
end
models/status.rb
class Status < ActiveRecord::Base
attr_accessible :content
has_many :mentions, dependent: :destroy
end
models/member.rb
class Member < ActiveRecord::Base
has_many :mentions, as: :mentionable, dependent: :destroy
end
controllers/mentions_controller.rb
class MentionsController < ApplicationController
before_filter :authenticate_member!
before_filter :load_mentionable
before_filter :find_member
def new
#mention = #mentionable.mentions.new
end
def create
#mention = #mentionable.mentions.new(params[:mention])
respond_to do |format|
if #mention.save
format.html { redirect_to :back }
else
format.html { redirect_to :back }
end
end
end
private
def load_mentionable
klass = [Status].detect { |c| params["#{c.name.underscore}_id"] }
#mentionable = klass.find(params["#{klass.name.underscore}_id"])
end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
end
config/routes.rb
resources :statuses do
resources :mentions
end
Thanks to this question: parse a post for #username I was able to get this working. My set up:
db/migrate/create_mentions.rb
class CreateMentions < ActiveRecord::Migration
def change
create_table :mentions do |t|
t.belongs_to :mentionable, polymorphic: true
t.belongs_to :mentioner, polymorphic: true
t.integer :status_id
t.integer :comment_id
t.timestamps
end
add_index :mentions, [:mentionable_id, :mentionable_type], :name => "ments_on_ables_id_and_type"
add_index :mentions, [:mentioner_id, :mentioner_type], :name => "ments_on_ers_id_and_type"
end
end
models/mention.rb
class Mention < ActiveRecord::Base
attr_accessible :mentioner_id, :mentioner_type, :mentionable_type, :mentionable_id, :status_id, :comment_id
belongs_to :mentioner, polymorphic: true
belongs_to :mentionable, polymorphic: true
end
models/member.rb
class Member < ActiveRecord::Base
has_many :mentions, as: :mentionable, dependent: :destroy
end
models/status.rb
class Status < ActiveRecord::Base
attr_accessor :mention
has_many :mentions, as: :mentioner, dependent: :destroy
after_save :save_mentions
USERNAME_REGEX = /#\w+/i
private
def save_mentions
return unless mention?
people_mentioned.each do |member|
Mention.create!(:status_id => self.id, :mentioner_id => self.id, :mentioner_type => 'Status', :mentionable_id => member.id, :mentionable_type => 'Member')
end
end
def mention?
self.content.match( USERNAME_REGEX )
end
def people_mentioned
members = []
self.content.clone.gsub!( USERNAME_REGEX ).each do |user_name|
member = Member.find_by_user_name(user_name[1..-1])
members << member if member
end
members.uniq
end
end
config/routes.rb
resources :statuses do
resources :mentions
end
helpers/mentions_helper.rb
module MentionsHelper
def statuses_with_mentions(status)
status.content_html.gsub(/#\w+/).each do |user_name|
member = Member.find_by_user_name(user_name[1..-1])
if member
link_to user_name, profile_path(member.user_name)
else
user_name
end
end
end
end

rails_admin - has_many association - default value

My Page model look like this:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug, :blocks
end
end
end
My Block model look like this:
class Block < ActiveRecord::Base
belongs_to :page
rails_admin do
edit do
field :title
field :body, :ck_editor
end
end
end
I needed workflow like this:
As an admin I click create page and I should see opened new block section with prefield title.
How can I create this scenario?
My own answer is realy dearty, but it works for me:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug
field :blocks do
# It is needed to show nested form
active true
end
end
end
# It is needed to create default block with title "main"
after_initialize do
if self.blocks.empty? && self.new_record?
self.blocks << Block.new(title: 'main')
end
end
# It is needed to prevent create default block when form has errors
after_validation do
return if(self.persisted? || self.blocks.empty?)
destroy_array = []
self.blocks.each do |block|
destroy_array << block if block.title == 'main' && block.body.nil?
end
self.blocks.destroy(destroy_array)
end
end

Rails Beginner Attempting TDD:

I'm new to unit testing and Rails in general. I've decided to build my projects in a TDD environment, but this has left me with some early questions.
I need help building the models to pass this test:
describe User do
it "should add user to team" do
team = Team.create(:name => "Tigers")
akash = User.create(:name => "Akash")
akash.teams << team
akash.memberships.size.should == 1
end
it "should allow buddyup"
john = User.create(:name => "John")
john.buddyup_with(akash)
john.memberships.size.should == 1
end
it "should validate linked buddys"
akash.buddys.should include(john)
end
end
Basically, ALL I want to do right now is pass the tests. Here is what I have so far:
class Team < ActiveRecord::Base
has_and_belongs_to_many :users
attr_accessubke :name
validates :name, :presence = true
:uniqueness => true
end
class User < ActiveRecord::Base
has_and_belongs_to_many: :teams
attr_accessible :name
validates :name, :presence = true
:uniqueness => true
end
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :users
end
end
class CreateTeams < ActiveRecord::Migration
def self.up
create_table :teams do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :teams
end
end
class CreateTeamsUsersJoinTable < ActiveRecord::Migration
def self.up
create_table :teams_users, :id => false do |t|
t.integer :team_id
t.integer :user_id
end
end
def self.down
drop_table :teams_users
end
end
That is all I have so far, and clearly it is nowhere near completion. Could you provide some insight, and perhaps code I should use to complete this? My biggest problem right now is the buddyup_with part. Adding a buddy will add a person to every team you are a member of, think of teams as parts of a development company, and buddys as understudies or something.
Suggestions I would make:
Use before do
# code #
end
to set up your conditions.
Do 1 test per. You have a lot going on there :)
Use Factory Girl.
Try what you have and work from there (Agile approach, even to adding tests).

ActiveRecord - automatically merging model data as an aggregate

Lets say I have two tables.
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :type, :default => 'User'
t.string :user_name, :null => false
t.boolean :is_registered, :default => true
# ... many more fields
end
end
end
class CreateContactInfo < ActiveRecord::Migration
def self.up
create_table :contact_info do |t|
t.integer :resource_id
t.string :resource_type
t.string :first_name
t.string :last_name
t.string :middle_initial
t.string :title
end
end
end
class ContactInfo < ActiveRecord::Base
belongs_to :contactable, :polymorphic => true
end
class User < ActiveRecord::Base
has_one :contact_info, :as => :contactable
# composed_of :contact_info # ... It would be nice if magics happened here
end
I would like to have the User's contact_info automatically merged into my User object as attributes of the user object without having to say #user.contact_info.first_name; instead, I would prefer to be able to write #user.first_name.
The reason I am breaking out attributes to the contact_info table is that these are common attributes to multiple models. That is why I am making setting up the contact_info as a polymorphic association.
Does anyone know of a good way to aggregate/merge the attributes of contact_info directly into my user model?
Use delegate:
class User < ActiveRecord::Base
has_one :contact_info, :as => :contactable
delegate :name, :name=, :email, :email=, :to => :contact_info
end
Not necessarily a good way to do it, but I did something similar by overriding the method_missing method and then calling my aggregated object. So, it would look something like:
class User
def method_missing(method_id)
self.contact_info.send(method_id)
end
end
Edit 1: Better implementation (I think this will work):
class User
alias_method :orig_method_missing, :method_missing
def method_missing(method_id)
if (self.contact_info.respond_to?(method_id))
self.contact_info.send(method_id)
else
orig_method_missing(method_id)
end
end
end
The above has the advantage that all other unknown method calls will get passed correctly.
I finally got it! Thank you both amikazmi and Topher Fangio. I had to implement both the delegate and method_missing techniques to get this to work.
Here is the total madness that finally ended up working for me! If anybody has suggestions on how to further improve this, I'd love to hear your suggestions.
class User < ActiveRecord::Base
attr_accessible *([:user_name, :udid, :password, :password_confirmation, :contact_info] + ContactInfo.accessible_attributes.to_a.map {|a| a.to_sym})
has_one :contact_info, :as => :contactable
def method_missing(method_id, *args)
if (!self.respond_to?(method_id) && self.contact_info.respond_to?(method_id))
self.contact_info.send(method_id, *args)
elsif (!self.class.respond_to?(method_id) && ContactInfo.respond_to?(method_id))
ContactInfo.send(method_id, *args)
else
super(method_id, *args)
end
end
# delegating attributes seems redundant with the method_missing above, but this secret sauce works.
ContactInfo.accessible_attributes.to_a.each do |a|
delegate a.to_sym, "#{a}=".to_sym, :to => :contact_info
end
def initialize(*args)
options = args.extract_options!
contact_attrs = ContactInfo.accessible_attributes.to_a.map{|a| a.to_sym}
#ci = ContactInfo.new(options.reject {|k,v| !contact_attrs.include?(k) })
super(*(args << options.reject { |k,v| contact_attrs.include?(k) }.merge(:contact_info => #ci) ) )
self.contact_info = #ci
end
validates_presence_of :user_name
validates_uniqueness_of :user_name
validates_associated :contact_info
def after_save
# automatically save the contact info record for the user after the user has been saved.
self.contact_info.save!
end
end
class ContactInfo < ActiveRecord::Base
set_table_name "contact_info"
belongs_to :contactable, :polymorphic => true
validates_presence_of :email
validates_uniqueness_of :email
attr_accessible :first_name,
:last_name,
:middle_initial,
:title,
:organization_name,
:email,
:email_2,
:twitter_name,
:website_url,
:address_1,
:address_2,
:city,
:state,
:zip,
:phone_work,
:phone_mobile,
:phone_other,
:phone_other_type
def full_name
[self.first_name, self.last_name].compact.join(' ')
end
end

Resources