How do I create a singleton global object in rails? - ruby-on-rails-3

I have an object that I want created once and accessible in one of my models. Where do I put him? I'm afraid if I put him in the model class file he'll get created every time I make a new instance of that model. I only want this object created once at start up. Here's the object:
require 'pubnub'
publish_key = 'fdasfs'
subscribe_key = 'sdfsdsf'
secret_key = 'fsdfsd'
ssl_on = false
pubnub_obj = Pubnub.new(publish_key,
subscribe_key,
secret_key,
ssl_on)
I use him like this in the model:
class Message < ActiveRecord::Base
def self.send_new_message_client(message)
message = { 'some_data' => message }
info = pubnub_obj.publish({
'channel' => 'testing',
'message' => message
})
puts(info)
end
end

In Rails, objects are recreated on each request. If this is some kind of service, it should be a singleton in the scope of a request.
Singleton objects should be created with the ruby singleton mixin:
require 'singleton'
class Pubnub
include Singleton
def initialize(publish_key, subscribe_key, secret_key, ssl_on)
# ...
end
def publish
# ...
end
end
Then you can call it with the instance method:
Pubnub.instance.publish
This way you make sure that this object will actually be a singleton (only one instance will exist).
You can place it safely in the models directory, though I often prefer the lib directory or maybe create a new directory for services. It depends on the situation.
Hope it helps!

If you want only one instance in your whole application, use a singleton, otherwise use a class variable.
To use a singleton, include the Singleton mixin.
require 'singleton'
class Pubnub
include Singleton
attr_writer :publish_key, :subscribe_key, :secret_key, :ssl_on
def publish
#...
end
end
and then use it like this:
require 'pubnub'
class Message < ActiveRecord::Base
Pubnub.instance.publish_key = 'xyz'
Pubnub.instance.subscribe_key = 'xyz'
Pubnub.instance.secret_key = 'xyz'
Pubnub.instance.ssl_on = 'xyz'
def self.send_new_message_client(message)
message = { 'some_data' => message }
info = Pubnub.instance.publish({
'channel' => 'testing',
'message' => message
})
puts(info)
end
end
You could also make it a class variable, to link it more tightly to a specific model:
require 'pubnub'
class Message < ActiveRecord::Base
##pubnub_obj = Pubnub.new('xyz', 'xyz', 'xyz', 'xyz')
def self.send_new_message_client(message)
message = { 'some_data' => message }
info = ##pubnub_obj.publish({
'channel' => 'testing',
'message' => message
})
puts(info)
end
end

Related

How to get the id of newly created record in rails & associate the id with another model - Rails

I'm building a rails app connected to a react app. The rails app has four models. For arguments sake here, let's pretend it has two models: a sender & address. The sender belongs_to an address, and an address has_many senders. Therefore a sender has a foreign_key of address_id. When the sender registers for a new account, he creates an address as well. I'm trying to build something in my rails controller whereby when a new address is created it is associated with address_id column of the sender. I tried building my own unique solution based on the solutions of this rails thread. but I didn't have much success. I implemented the following in my address controller:
class AddressesController < ActionController::API
def show
#address = Address.find(params[:id])
end
def create
success = false
address = Address.new(address_params)
sender = #sender
if address.save
address_id = address.sender.build({address_id: address.id })
success = true if !address_id.empty?
else
render json: { errors: address.errors.full_messages }, status: :bad_request
end
if success = true
render json: {status: 'address created successfully'}, status: :created
else
render('edit')
end
end
def address_params
params.require(:address).permit(:streetname, :zipcode, :city, :country)
end
end
And I'm getting this error in the terminal:
NoMethodError (undefined method `sender' for #<Address:0x00007f93b2576050>
Did you mean? senders
senders=
send):
app/controllers/addresses_controller.rb:21:in `create'
So I placed an attr_accessor in my senders controller:
class SendersController < ApplicationController
attr_accessor :sender
def show
#sender = Sender.find(params[:id])
end
def create
#sender = Sender.new(sender_params)
if #sender.save
render json: {status: 'sender created successfully'}, status: :created
else
render json: { errors: sender.errors.full_messages }, status: :bad_request
end
end
def sender_params
params.require(:sender).permit(:userid, :name, :email, :photourl, :address_id)
end
end
I feel I'm over complicating things over here. So I really need help with this one. Thanks in Advance!
NoMethodError (undefined method `sender' for)
First, the error triggered because you have has_many relation with sender, so it should be senders not sender
address.senders.build
When the sender registers for a new account, he creates an address as
well. I'm trying to build something in my rails controller whereby
when a new address is created it is associated with address_id column
of the sender.
Second, your approach is wrong. You can make use of available methods for belongs_to association. For example, in your situation, you can call #sender.build_address
address = #sender.build_address(address_params)
The build_association method returns a new object of the associated
type. This object will be instantiated from the passed attributes, and
the link through this object's foreign key will be set, but the
associated object will not yet be saved.
So, finally the create would look like below(after removing unnecessary code)
def create
address = #sender.build_address(address_params)
if address.save
render json: {status: 'address created successfully'}, status: :created
else
render json: { errors: address.errors.full_messages }, status: :bad_request
end
end
You don't need to manually set the id if you're creating a record via an association:
address.sender.build({address_id: address.id })
Can just be
address.sender.build
And rails will assign the id for you.

Rails force all translations

I am using globalize3 with rails_admin thanks to this gist. What bugs me, is that the user can add as many translations as he wants.
Moreover, he isn't forced to translate the content in every single locale (as in I18n.available_locales). I'd like that. How can you tackle such a situation?
Models (shortened):
class Project < ActiveRecord::Base
has_many :project_translations, :dependent => :destroy, :inverse_of => :project
accepts_nested_attributes_for :project_translations, :allow_destroy => true
class ProjectTranslation < ActiveRecord::Base
belongs_to :project
I ended up using Active Admin plus activeadmin-globalize3 instead. Much easier.
It bugged me too, so I created custom field type that doesn't allow it.
The main class:
module RailsAdmin
module Config
module Fields
module Types
class GlobalizeTabs < RailsAdmin::Config::Fields::Association
RailsAdmin::Config::Fields::Types::register(:globalize_tabs, self)
register_instance_option :partial do
:form_globalize_tabs
end
def method_name
"#{super}_attributes".to_sym
end
# Reader for validation errors of the bound object
def errors
bindings[:object].errors[name]
end
def available_locales
I18n.available_locales
end
def current_locale
I18n.locale
end
# Returns array of Translation objects
# It gets existing or creates new empty translation for every locale
# It's used in fields_for method in partial
def translations
translated_locales = #bindings[:object].translated_locales
available_locales.collect do |locale|
translated_locales.include?(locale) ? #bindings[:object].translation_for(locale) : #bindings[:object].translations.new({ locale: locale })
end
end
end
end
end
end
end
It inherits from RailsAdmin::Config::Fields::Association class, because it uses very similar to _form_nested_many partial (that's used in has_many type).
The partial:
.controls
= form.errors_for(field)
%ul.nav.nav-tabs{ :style => 'margin-top:5px' }
- field.available_locales.each do |locale|
%li{ class: ( 'active' if locale == field.current_locale ) }
%a{ href: "##{locale}", data: { toggle: "tab" } }= locale
.tab-content
= form.fields_for field.name, field.translations, wrapper: false do |nested_form|
.fields.tab-pane{ id: nested_form.object.locale, class: ( 'active' if nested_form.object.locale == field.current_locale ) }
= nested_form.generate({:action => :nested, :model_config => field.associated_model_config, :nested_in => field.name })
= form.help_for(field)
It uses field.translations method from the custom field class, that returns an array of Translation objects.
Every Translation object corresponds to available locale, and it's either an existing object from the database (if translation already exists) or new empty translation object.
E.g.
You've got this available locales:
I18n.available_locales = [:en, :cz, :ru]
You have Page model which includes some translated fields.
Also, you have an object of the class Page (a row in the database), that has translations for :en and :cz locales, but lacks one for the :ru.
So, field.translations method inside _form_globalize_tabs partial returns an array that contains:
2 existing translations for :en and :cz and 1 just initialized translation for :ru.
In the partial I'm passing this array to the fields_for helper method from nested_form gem, that returns 3 fieldsets for every translation object.
You can use this gem, if you don't want to mess with the code yourself: https://github.com/scarfaceDeb/rails_admin_globalize_field

rails - Create new job within a delayed job

I am creating a delayed job in my controller using the delay method as below:
JobProcessor.delay(:run_at => Time.now).process_job(options)
Now inside the process_job method I am doing
chunks = options[:collection].each_splice(10).to_a
chunks.each do |chunk|
self.delay(:run_at => Time.now).chunk_job
end
This is giving me error stack level too deep when I send request to the URL
What might be the issue? Please help.
I was able to make it work for me by doing some changes.
I changed the outer method process_job to be an instance method
And kept the rest of the code as it is.
So now my code looks like
JobProcessor.new.process_job(options)
in the controller, and inside the JobProcessor class I have
class JobProcessor
def process_job(options)
chunks = options[:collection].each_splice(10).to_a
chunks.each do |chunk|
self.class.delay(:run_at => Time.now).chunk_job(options)
end
end
handle_asynchronously :process_job, :run_at => Proc.new { 2.seconds.from_now }
def self.chunk_job(options)
# perform chunk job here
end
end
This works for me.

Custom validator in client_side_validations

I need to add custom validator for comparing two dates - start date and end date.
I created custom validator
class MilestoneDatesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if record.start_date > record.end_date
record.errors.add(attribute, :end_date_less, options.merge(:value => value))
end
end
end
And I created ClientSideValidations custom validator. I wasn't sure how I can get another attributes values in it, but I tried to do this in such way:
ClientSideValidations.validators.local['milestone_dates'] = function(element, options) {
start_date = new Date($('#milestone_start_date').val());
end_date = new Date($('#milestone_end_date').val());
if(end_date < start_date) {
return options.message;
}
}
But it doesn't work/ I have error only after reloading page, but not with client side validations.
I use client_side_validations (3.2.0.beta.3), client_side_validations-formtastic (2.0.0.beta.3), rails (3.2.3)
The code you supplied above is missing any mention of a declaration (and use) of a validator helper method. In the milestone_dates_validator.rb initializer try adding the following at the end of the file:
module ActiveModel::Validations::HelperMethods
def validates_milestone_dates(*attr_names)
validates_with MilestoneDatesValidator, _merge_attributes(attr_names)
end
end
And in your model call the validator on the attribute/s you are validating:
validates_milestone_dates :milestone_ends_at

how modify session in model?

I am using Rails 3. I was coding a controller very verbose. So i am trying refactoring the controller.
I coded a class called ProductMaker which make a product and modify session (product task for wizard form as current_step, if the request is a refresh, etc)
This class has method that receiving session as parameter, modify and then return this new session.
Controller action:
def new
#INITIALIZE CODE
session[:refresh] ||= SortedArray.new [1]
#...MORE CODE
end
def create
#...MUCH CODE
unless Utilities.is_refresh(session[:refresh])
#...more code
session = ProductMaker.some_method_which_return_session(session) #KEY PROBLEM LINE
#...more code
end
#... MORE CODE
end
My ProductMaker class in lib folder:
class ProductMaker
def self.some_method_which_return_session(session)
session[:any_key] = "some value"
return session
end
end
However when I write the KEY PROBLEM LINE the session is a nil value. If i comment this line the session is a ActionDispatch::Session::AbstractStore::SessionHash.
Which could be the problem?
How could i refactoring controller logic, that modify many session keys and 'fill' a model depending the session values, to model/class ?
UPDATE:
I am reading about binding in ruby.
How could modify the session using bindings and eval method?
If you have other ideas, please post your answer.
Thanks in advance
You are assigning session = on the "key problem line". This creates a local variable for the create method which shadows the session method on ActionController::Base. The local variable is nil before you assign it (not non-existent!) so the end result is that session == nil for the whole method, which obviously breaks everything. So: don't assign session. Call your return value something else.
I use binding technique. So i pass the context (simulating parameter pass reference variables).
Example
class OwnController < ApplicationController
def my_action
SettingManager.modify("session[:session]", binding)
end
end
class SettingManager
def self.modify(session, binding)
eval "#{session}.any_value = 5" binding
eval "#{session}.other_value = 'value'", binding
end
end
Finally I use bindings.
For example:
class SessionManager
self.update(session, binding)
eval "#{session} = 0", binding
end
end
class SomeController < ApplicationController
def foo_action_1
session[:refresh] = 1
end
def example_ajax_modify_session
a = session[:refresh] == 1 #true
SessionManager.update("session[:refresh]", binding)
b = session[:refresh] == 1 #false
a == b #false because a == 1 and b == 0
end
I had to modify and manage many session values so before I has a very verbose controller.
Now I could refactoring this logic to model.