Mailboxer allows you to connect multiple models as in the example from the gem page. Mailboxer Github page
You can use Mailboxer in any other model and use it in several different models. If you have ducks and cylons in your application and you want to exchange messages as if they were the same, just add acts_as_messageable to each one and you will be able to send duck-duck, duck-cylon, cylon-duck and cylon-cylon messages.
How can we restrict messaging to only between duck-cylon and vice versa? So, only a duck can initiate a conversation and a cylon can reply? And, no duck-duck and cylon-cylon conversations are possible?
You could add a module to the models
class Duck < ActiveRecord::Base
acts_as_messageable
include mailboxer_filter
end
and
class Cylon < ActiveRecord::Base
acts_as_messageable
include mailboxer_filter
end
your module...
module MalboxerFilter
def initiator?
self.class == Duck
end
def replyer?
self.class == Cylon
end
def send_message_filtered(beta, body, subject)
self.send_message(beta, body, subject) if initiator? && beta.replyer?
end
def reply_to_sender_filtered(*args)
self.reply_to_sender(*args) if replyer?
end
end
Then use send_message_filtered and reply_to_sender_filtered in your app. This can be more sophisticated if you need it... perhaps raise an exception if a Cylon attempts to initiate a message or a Duck attempts to reply.
Related
I'm writing the documentation about my Rails' project and I have a question.
I developed the notification's mechanism with the GoRails tutorial and the code is something like this, Me question is this is a controller or a model logic? I think controller logic but I am not sure
class EventNotification < Noticed::Base
deliver_by :database
def message
#group=Group.find(params[:group][:id])
#user=User.find(params[:user][:id])
#event=Event.find(params[:event][:id])
"#{#event.user.username} invite you in #{#event.title} into #{#group.name} click here to visit"
end
#
def url
group_event_path(Group.find(params[:group][:id]), Event.find(params[:event][:id]) )
end
end
I have the following model:
class TwitterEngagement < ApplicationRecord
end
And I would like to override create (and create!), update (and
update!) methods of it so no one can manually entry fake data. I would like the help of someone more experienced with active record and rails so I don't mess anything up. Right now what I have is:
class TwitterEngagement < ApplicationRecord
belongs_to :page
def create
super(metrics)
end
def update
super(metrics)
end
private
def metrics
client.get_engagements(page.url)
def client
TwitterClient.new
end
end
Thank you.
TL;DR:
class FacebookEngagement < ApplicationRecord
def create_or_update(*args, &block)
super(metrics)
end
Probably depends on your Rails version, but I traced the ActiveRecord::Persistence sometime before in Rails 5, and found out that both create and update eventually calls create_or_update.
Suggestion:
If ever possible, I'll just do a validation, because it kinda makes more sense because you are validating the inputs, and then probably set an optional readonly?, to prevent saving of records. This will also prevent "silent failing" code / behaviour as doing TL;DR above would not throw an exception / populate the validation errors, if say an unsuspecting developer does: facebook_engagement.update(someattr: 'somevalue') as the arguments are gonna basically be ignored because it's instead calling super(metrics), and would then break the principle of least surprise.
So, I'll probably do something like below:
class FacebookEngagement < ApplicationRecord
belongs_to :page
validate :attributes_should_not_be_set_manually
before_save :set_attributes_from_facebook_engagement
# optional
def readonly?
# allows `create`, prevents `update`
persisted?
end
private
def attributes_should_not_be_set_manually
changes.keys.except('page_id').each do |attribute|
errors.add(attribute, 'should not be set manually!')
end
end
def set_attributes_from_facebook_engagement
assign_attributes(metrics)
end
def metrics
# simple memoization to prevent wasteful duplicate requests (or remove if not needed)
#metrics ||= graph.get_object("#{page.url}?fields=engagement")
end
def graph
Koala::Facebook::API.new
end
end
I have several after_create methods (mostly to deliver emails or messages on the platform) and they call jobs which eventually call methods, but my code is starting to be structured like this which doesn’t really look nice
class Message < ApplicationRecord
after_create: :deliver_message_job
def deliver_message_job
DeliverMessageJob.perform_later self.id
end
def deliver_message
# logic to deliver message
end
end
and in the job, I just call the method
class DeliverMessageJob < ApplicationJob
queue_as :default
def perform(message_id)
Message.find(message_id).deliver_message
end
end
Is there a better way to go about structuring this?
If you want to continue using the callback, then you can pass a block that does what the Message#deliver_message_job method is doing for you so that you don't need to write out that method since it offers you little but a handle for the callback.
class Message < ApplicationRecord
after_create { |msg| DeliverMessageJob.perform_later(msg.id) }
# . . .
end
If you want to get rid of the Message#deliver_message method, then maybe you could put the logic for delivering the message into DeliverMessageJob#perform instead. This may make more sense semantically since it's purpose is clearly just to deliver a message.
class DeliverMessageJob < ApplicationJob
queue_as :default
def perform(message_id)
message = Message.find(message_id)
# Copy over/refactor the Message#deliver_message logic.
# Do some stuff with the message to deliver it . . .
end
end
It is worth asking yourself "Should a message know how to deliver itself?" The answer is probably either "No, it shouldn't" or "It's not important", in which case just let DeliverMessageJob worry about the details. This way, you have removed a couple of methods from the Message model and slimmed it out a bit, and made your classes neater and simpler.
I'm coming from the .NET world and I'm trying to figure out what the 'Rails Way' to pass an object across tiers in a multi-tier application.
I'm writing a multi carrier pricing API. Basically in my price controller I have access to the following parameters params[:carrier], params[:address_from], params[:address_to], params[:container_type], etc. I have a validation library, a compliance library and a price-finder library that each deal with a subset of the params.
In .NET the params would be encapuslated in data transfer objects (DTOs) or contracts. Before calling any of the libraries, they would be converted to domain objects (DOs) and each library would work on the DOs, thus avoiding a tight coupling on the DTOs. Ruby programming recommands the use of 'duck typing', so my libraries could work directly on params (even though you would access symbols and not objects/properties). Or should I marshall my params into a PriceRequest object and have my libraries work on the PriceRequest type?
Option 1:
class PricesController < ApplicationController
def get
CarrierValidator.validate(params)
...
end
end
class CarrierValidator
def self.validate(params)
raise CarrierError if !Carrier.find_by_name(params[:carrier_name]).exists?
end
end
Option 2:
class PricesController < ApplicationController
def get
pricesRequest = PricesRequest.new(carrier_name: params[:carrier_name], ...)
pricesRequest.validate
...
end
end
class PriceRequest
attr_accessor : ...
def initalize
...
end
def validate
CarrierValidator.validate(self.carrier_name)
end
end
class CarrierValidator
def self.validate(carrier_name)
raise CarrierError if !Carrier.find_by_name(carrier_name).exists?
end
end
TIA,
J
You should create a type. I would use ActiveModel to encapsulate the data (attributes) & business logic (validations & maybe some layer-specific methods for processing the data).
Basically, you want to be able to do Rails-y things in the controller like:
def get
price_request = PriceRequest.new(params[:price_request])
if price_request.valid?
# do something like redirect or render
else
# do something else
end
end
so you want to declare:
class PriceRequest
include ActiveModel::Model
attr_accessor :carrier, :address_from, :address_to, :container_type
validates :carrier, presence: true
validate :validate_address_from
def validate_address_from
# do something with errors.add
end
# and so on
This is a good place to start: http://edgeguides.rubyonrails.org/active_model_basics.html
More details in the API: http://api.rubyonrails.org/classes/ActiveModel/Model.html
Hope that points you in the right direction...
I was recommend in an earlier question to use a gem called Wisper. I am very happy to learn about it, as it is exactly the solution I'm looking for. What I can't understand from the documentation on Wisper is how listeners register themselves.
Here is my code:
app/models/subscription.rb
class Subscription < ActiveRecord::Base
include Wisper::Publisher
def some_method
# some code here
broadcast(:subscription_paused)
end
end
app/models/offer.rb
class Offer < ActiveRecord::Base
def subscription_paused
binding.pry # or whatever
end
end
So I'm not entirely sure about this part here. I've tried a variety of subscribing techniques, but I think it just comes down to me not really understanding this aspect of it:
config/initializers/wisper.rb
Wisper.subscribe(Offer.new)
I also tried, similar to the example in the Wiki:
subscription = Subscription.new
subscription.subscribe(Offer.new)
What am I missing here? (I'm not really sure if the above code should even go in an initializer.)
If the tables exists for Offer and Subscription model then the code should work.
Try this in the rails console:
# class Subscription < ActiveRecord::Base
class Subscription
include Wisper::Publisher
def some_method
# some code here
broadcast(:subscription_paused)
end
end
# class Offer < ActiveRecord::Base
class Offer
def subscription_paused
puts "jeijjj"
end
end
Wisper.subscribe(Offer.new)
Subscription.new.some_method
It should generate an output:
"jeijjj"