Using current_user to customize ActionCable channel submittion - websocket

I'm making instant commenting with Rails 5. Using Rails *.js.erb, which is comfortable for this size of project. Also use ActionCable to deliver new comments to users. The thing is that I want ActionCable to send rendered comments/_comment.html.haml directly to client to evaluate this code in browser. In common case it's totally achivable. But in erb template of comment I have to deal with current_user to add delete link if current_user.admin? The problem is when I invoke CommentsChannel.broadcast_to and render _comment.html.haml renderer do not now current_user.
Question is how can I render _comment.html.haml and submit it to each separate subscription defined by current user, using current user from this subscription?
Devise is used for authentication.
comments/_comment.html.haml
%div[comment]
.details
%span.author= comment.user.email
%span.time= l comment.created_at, format: :short
= comment.body
= link_to t('common.reply'), comment, class: 'reply-link'
= link_to t('common.delete'), comment, method: :delete, remote: true if current_user.admin?
= render 'comments/form', comment: comment.commentable.comment_threads.build, parent: comment
- if comment.children.any?
.child-comments
= render comment.children.sort_by { |c| c.created_at }.reverse
application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
def find_verified_user
if verified_user = env['warden'].user
verified_user
else
reject_unauthorized_connection
end
end
end
end
broadcast_comment_job.rb
class BroadcastCommentJob < ApplicationJob
queue_as :default
def perform(comment)
CommentsChannel.broadcast_to \
comment.commentable,
comment: render_comment(comment)
end
private
def render_comment(comment)
CommentsController.render(partial: 'comment', locals: { comment: comment })
end
end
comment.rb
class Comment < ActiveRecord::Base
...
after_create_commit { BroadcastCommentJob.perform_now(self) }
...
end

Related

Undefined method `detailsFileTag' for nil:NilClass

My aim is to test a design pattern—in this case the decorator pattern.
I have taken the below code from the lib/ folder and controller folder.
The library file here has a BasicTag class which is inherited by class TaggingDecor and an another class DescriptionDecor.
Now all these three classes have a common function called detailsForTag.
This function when is called from controller file as listed below.
The error is
undefined method `detailsFileTag' for nil:NilClass
and the error line is as tagged below in the code.
# the concrete component we would like to decorate
class BasicTag
def initialize()
#init_basicTag = "File with no tags"
end
# getter method
def detailsFileTag
return " test from BasicTag....."
end
end
# decorator class -- this serves as the superclass for all the concrete decorators
# the base/super class decorator (i.e. no actual decoration yet), each concrete decorator (i.e. subclass) will add its own decoration
class TaggingDecor
def initialize(r_file)
#r_file = r_file
#aTagg = "no tag added"
#aDescTag = "no description added"
end
# override the details method to include the description of the extra feature
def detailsFileTag
return #aTagg + #aDescTag
end
end
# a concrete decorator
class TagDec < TaggingDecor
def initialize(r_file)
super(r_file)
#aTagg = "no tag added"
#aDescTag = "tagdec description added"
end
# override the details method to include the description
def detailsFileTag
return #aTagg + #aDescTag
end
end
# another concrete decorator
class DescriptionDec < TaggingDecor
def initialize(r_file)
super(r_file)
#aTagg = "no tag added"
#aDescTag = "descriptiondec description added"
end
# override the details method to include the description
def detailsFileTag
return #aTagg + #aDescTag
end
end
============================= controller file ============================
# POST /file_taggings
# POST /file_taggings.json
def create
#file_tagging = FileTagging.new(file_tagging_params)
#####################################################
#rails g scaffold fileTagging filename:string filetag:string filedescription:string
# information For logging the file details into table
#file_tagging.filename = params[:file_tagging][:filename]
#file_tagging.filetag = params[:file_tagging][:filetag]
#file_tagging.filedescription = params[:file_tagging][:filedescription]
########################################################################
# information For logging the file details
# create an instance/object of a BasicTag
myTagg = BasicTag.new
# add the extra features to the new car
if params[:file_tagging][:filetag].to_s.length > 0 then
myTagg = TagDec.new(myTagg)
end
#if params[:file_tagging][:description].to_s.length > 0 then
# myTagg = DescriptionDec.new(myTagg)
#end
## populate the Description details - By calling the BASE class and SUPER class as stated above.
#Error is here for the call myTagg.detailsFileTag !
#file_tagging.filedescription = myTagg.detailsFileTag
# retrieve the instance/object of the MyLogger class
logger = MyLogger.instance
logger.logInformation("A new file details are: " + #file_tagging.filesdescription)
logger.logInformation("A new file details are: ")
#####################################################
respond_to do |format|
if #file_tagging.save
format.html { redirect_to #file_tagging, notice: 'File tagging was successfully created.' }
format.json { render :show, status: :created, location: #file_tagging }
else
format.html { render :new }
format.json { render json: #file_tagging.errors, status: :unprocessable_entity }
end
end
end
============================================================
Yes correct its a nill object. So I had by mistake deleted out the ruby file in the model folder, which was created by the scaffold.
model>> FileTagging.rb.

Ruby Quick Class Instance Scope

I have a quick problem that probably comes down to something stupid. I have a class that extends OAuth::AccessToken and uses instance variables (#) so that each time it constructs an object, those variables will be unique that instance. However, when I try to return the final object from this class, I get an error. A quick example:
require 'oauth'
class OauthFigshare < OAuth::AccessToken
def initialize (consumerkey, consumersecret, accesstoken, accesstokensecret)
#consumerkey = consumerkey
#consumersecret = consumersecret
#accesstoken = accesstoken
#accesstokensecret = accesstokensecret
#apiurl = "http://api.figshare.com"
#consumer = OAuth::Consumer.new(#consumerkey,#consumersecret,{:site=> #apiurl})
#token = { :oauth_token => #accesstoken, :oauth_token_secret => #accesstokensecret}
puts #consumer.class
puts #token
#client = OAuth::AccessToken.from_hash(#consumer, #token)
puts #client
puts #client.get('/v1/my_data/articles')
return #client
end
end
The problem is that when I check inside the class to see if the token is working, it does. However, when I check against the constructed object outside the class, it doesn't work.
#client.get(url) returns Net::HTTPOk calling from in the class
auth = OauthFigshare.new(inputs)
auth.get(url)
This returns Net::HTTPUnauthorized
What am I not getting about scope here?
Edit to include actual class
The return value of the initialize method is not used. It seems like you actually want to override self.new instead.

Is there a better way to test this Ruby class with RSpec?

I'm extracting a subset of fields from a full JSON dataset having a JSON fixture. The better way I could think of is the following :
require "spec_helper"
# API ref.: GET /repos/:owner/:repo
# http://developer.github.com/v3/repos/
describe Elasticrepo::RepoSubset do
context "extract a subset of repository fields" do
let(:parsed) { Yajl::Parser.parse(fixture("repository.json").read) }
subject { Elasticrepo::RepoSubset.new(parsed) }
context "#id" do
its(:id) { should eq(2126244) }
end
context "#owner" do
its(:owner) { should eq("twitter") }
end
context "#name" do
its(:name) { should eq("bootstrap") }
end
context "#url" do
its(:url) { should eq("https://api.github.com/repos/twitter/bootstrap") }
end
context "#description" do
its(:description) { should eq("Sleek, intuitive, and powerful front-end framework for faster and easier web development.") }
end
context "#created_at" do
its(:created_at) { should eq("2011-07-29T21:19:00Z") }
end
context "#pushed_at" do
its(:pushed_at) { should eq("2013-04-13T03:56:36Z") }
end
context "#organization" do
its(:organization) { should eq("Organization") }
end
context "#full_name" do
its(:full_name) { should eq("twitter/bootstrap") }
end
context "#language" do
its(:language) { should eq("JavaScript") }
end
context "#updated_at" do
its(:updated_at) { should eq("2013-04-13T19:12:09Z") }
end
end
end
but I wonder if is there a better, smarter, cleaner or just more elegant way of doing that. The class I TDD out is this :
module Elasticrepo
class RepoSubset
attr_reader :id, :owner, :name, :url, :description, :created_at, :pushed_at,
:organization, :full_name, :language, :updated_at
def initialize(attributes)
#id = attributes["id"]
#owner = attributes["owner"]["login"]
#name = attributes["name"]
#url = attributes["url"]
#description = attributes["description"]
#created_at = attributes["created_at"]
#pushed_at = attributes["pushed_at"]
#organization = attributes["owner"]["type"]
#full_name = attributes["full_name"]
#language = attributes["language"]
#updated_at = attributes["updated_at"]
end
end
end
I would remove the individual context blocks. They don't add any additional information.
I'd use a map of keys/values and iterate, or create an object with the correct values and compare the entire object.

Using ActiveRecord with DelegateClass

Using rails 3.2.8 I have a model setup with a couple attributes
class MyModel < ActiveRecord::Base
attr_accessible :foo, :bar
end
I have another class setup using the above as a delegate class
class MyModelPresenter < DelegateClass(MyModel)
def initialize(month, obj)
#month = month
super(obj)
end
def self.build(month, attributes = { })
new(month, MyModel.new).tap do |p|
p.attributes = attributes
end
end
def attributes=(attributes)
attributes.each { |k, v| send("#{k}=", v) }
end
end
When I create a new MyModelPresenter like so:
MyModelPresenter.build(Date.today, {:foo => 1})
I get the following back
NoMethodError: undefined method `foo=' for #<MyModel:0x1098f31a8>
from /Users/me/.rbenv/versions/ree-1.8.7-2011.03/lib/ruby/gems/1.8/gems/activemodel-3.2.8/lib/active_model/attribute_methods.rb:404:in `method_missing'
from /Users/me/.rbenv/versions/ree-1.8.7-2011.03/lib/ruby/gems/1.8/gems/activerecord-3.2.8/lib/active_record/attribute_methods.rb:149:in `method_missing'
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `send'
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `attributes='
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `each'
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:23:in `attributes='
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:17:in `build'
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:15:in `tap'
from /Users/me/dev/temp/app/presenters/my_model_presenter.rb:15:in `build'
For some reason the database attributes on the model aren't getting defined (setters or getters). Before upgrading to rails 3.2 this was all working in a rails 3.1 app.
Does anyone have any idea why the model's attribute methods aren't getting defined?
Inheriting from Delegator instead of using DelegateClass solved the problem. Here is what the final class looked like:
class MyModelPresenter < Delegator
def initialize(month, obj)
#month = month
super(obj)
#_sd_obj = obj
end
def __getobj__
#_sd_obj
end
def self.build(month, attributes = { })
new(month, MyModel.new).tap do |p|
p.attributes = attributes
end
end
def attributes=(attributes)
attributes.each { |k, v| send("#{k}=", v) }
end
end
As you can see I also had to add the def __getobj__ method. and set #_sd_obj in the initializer to an instance of the class I'm delegating from.
See the SimpleDelegator example.

Gmaps4rails Polygon not displaying [duplicate]

Ruby noob here. Trying to display a list of points as a polygon on a google map using the gmaps4rails gem (awesome gem by the way). Any suggestions based on code sample below would be much appreciated! I can see the outline for the map, but no map and no polygon. Update: this code has been updated and the problem is solved.
Class Schoolpoint is a list of lat/long pairs that belong to School
In my controller:
#polyjson = []
schoolpoints = []
Schoolpoint.where(:school_id => params[:id]).each do |point|
schoolpoints << { :lng => point.longitude, :lat => point.latitude}
end
#polyjson = [schoolpoints]
#polyjson = #polyjson.to_json
Then in the view:
<%= gmaps({"polygons" => { "data" => #polyjson }})
in Schoolpoint model:
class Point < ActiveRecord::Base
belongs_to :place
acts_as_gmappable :process_geocoding => false
def gmaps4rails_address
"#{longitude}, #{latitude}"
end
end
Update: This error now longer exists, but I've left it in case its helpful to someone dealing with similar problem. Finally, here is the resulting js with an uncaught Syntaxerror => unexpected token.
Gmaps.map = new Gmaps4RailsGoogle();
Gmaps.load_map = function() {
Gmaps.map.initialize();
Gmaps.map.polygons = [[{"lng"=>-80.190262, "lat"=>25.774252, "strokeColor"=>"#FF0000", "strokeOpacity"=>0.3, "strokeWeight"=>1, "fillColor"=>"#FF0000", "fillOpacity"=>0.7}, {"lng"=>-87.6245284080505, "lat"=>41.8868315803506}, {"lng"=>-87.6241636276245, "lat"=>41.8674515900783}, {"lng"=>-87.6203870773315, "lat"=>41.8674835487326}, {"lng"=>-87.6167392730712, "lat"=>41.8579591627635}, {"lng"=>-87.6348495483398, "lat"=>41.8577034549953}, {"lng"=>-87.6342701911926, "lat"=>41.8588701133785}, {"lng"=>-87.6341199874878, "lat"=>41.858946025344}, {"lng"=>-87.6341146230697, "lat"=>41.8590858629394}, {"lng"=>-87.6341199874878, "lat"=>41.8600767034266}, {"lng"=>-87.6342219114303, "lat"=>41.8612433185139}, {"lng"=>-87.634157538414, "lat"=>41.8613112372298}, {"lng"=>-87.6342540979385, "lat"=>41.8621502271823}, {"lng"=>-87.6341950893402, "lat"=>41.8622580965204}, {"lng"=>-87.6342433691024, "lat"=>41.8626336402037}, {"lng"=>-87.6341092586517, "lat"=>41.8630930789441}, {"lng"=>-87.6342326402664, "lat"=>41.8631010691539}, {"lng"=>-87.6342862844467, "lat"=>41.8651984646832}, {"lng"=>-87.6342165470123, "lat"=>41.865314318812}, {"lng"=>-87.6342540979385, "lat"=>41.865929540668}, {"lng"=>-87.6343238353729, "lat"=>41.8661652409794}, {"lng"=>-87.6343667507171, "lat"=>41.8664728485533}, {"lng"=>-87.6342701911926, "lat"=>41.866564731048}, {"lng"=>-87.6343882083892, "lat"=>41.8673317449823}, {"lng"=>-87.6344525814056, "lat"=>41.8680388278011}, {"lng"=>-87.6346457004547, "lat"=>41.8691693450993}, {"lng"=>-87.6346671581268, "lat"=>41.8696886572982}, {"lng"=>-87.6345813274383, "lat"=>41.8698804022745}, {"lng"=>-87.6347583532333, "lat"=>41.869992253245}, {"lng"=>-87.634892463684, "lat"=>41.8706873227465}, {"lng"=>-87.6353269815445, "lat"=>41.8726167002032}, {"lng"=>-87.6352626085281, "lat"=>41.8728443868687}, {"lng"=>-87.6354557275772, "lat"=>41.8730081609862}, {"lng"=>-87.6353698968887, "lat"=>41.8732797854267}, {"lng"=>-87.6356971263885, "lat"=>41.8740227522642}, {"lng"=>-87.6356971263885, "lat"=>41.8746458790817}, {"lng"=>-87.6359224319458, "lat"=>41.87509724279}, {"lng"=>-87.6361316442489, "lat"=>41.8754088017203}, {"lng"=>-87.6364105939865, "lat"=>41.8754727110567}, {"lng"=>-87.6364642381668, "lat"=>41.8757642965932}, {"lng"=>-87.6371240615844, "lat"=>41.876678987795}, {"lng"=>-87.637939453125, "lat"=>41.8801059676767}, {"lng"=>-87.6379930973053, "lat"=>41.8806172030015}, {"lng"=>-87.6378536224365, "lat"=>41.8829017358812}, {"lng"=>-87.6375961303711, "lat"=>41.8844593251054}, {"lng"=>-87.6372849941253, "lat"=>41.8857213439117}, {"lng"=>-87.6371347904205, "lat"=>41.8860408383893}, {"lng"=>-87.6355576515197, "lat"=>41.8870552227663}, {"lng"=>-87.6282513141632, "lat"=>41.8870951588295}, {"lng"=>-87.6281654834747, "lat"=>41.8868076186168}]];
153:2439 Uncaught SyntaxError: Unexpected token >
Gmaps.map.create_polygons();
Gmaps.map.adjustMapToBounds();
Gmaps.map.callback();
};
window.onload = function() { Gmaps.loadMaps(); };
I passed their base tutorial (from screen cast) and markers works fine. But I had problem with polylines (as you with polygons). At the end I resolved my problem (it can help you with polygons).
So, view is the same as they gave :
<%= gmaps({
"polylines" => { "data" => #bemap_polylines }
})
%>
The model is also the same as their.
class Character < ActiveRecord::Base
belongs_to :bemap
acts_as_gmappable
def gmaps4rails_address
#describe how to retrieve the address from your model, if you use directly a db column, you can dry your code, see wiki
address
logger.info address
end
end
So, the main problem was into controller. Here is mine :
def show
#bemap = Bemap.find(params[:id])
#bemap_polylines = []
#bemap_characters = []
#bemap.characters.each do |v|
#bemap_characters << { :lng => v[:longitude], :lat => v[:latitude]}
end
#bemap_polylines << #bemap_characters
#bemap_polylines = #bemap_polylines.to_json
respond_to do |format|
format.html # show.html.erb
format.json { render json: #bemap }
end
end
Gmaps4rails doesn't provide any builtin method to create the js for polygons.
The one you use is malformed, see doc: https://github.com/apneadiving/Google-Maps-for-Rails/wiki/Polygons.

Resources