I'm trying to 'MonkeyPatch' this controller in my implementation so that it can handle a third parameter ('productname').
The original activate method in the gem reads
def activate
if Digest::MD5.hexdigest(params["security_data"] + SaasySimple.config.secret) == params["security_hash"]
SaasySimple.config.model.activate( params['token'], params['id'] )
end
end
My entire new file, placed in lib/monkeys/sassysimple.rb, reads
module SaasySimple
class SubscriptionsController < ApplicationController
def activate
if Digest::MD5.hexdigest(params["security_data"] + SaasySimple.config.secret) == params["security_hash"]
SaasySimple.config.model.activate( params['token'], params['id'], params['productname'] )
end
end
end
end
This isn't working - I'm still getting an error of subscriptions#activate (ArgumentError) "wrong number of arguments (2 for 3)", which I believe is caused because my user method (see below) is expecting productname but not getting it from the un-monkeypatched version. Can someone tell me why putting the file in lib isn't working? Thanks!
This is the user method:
def self.activate(token, id, productname)
user = User.find( id )
user.token = token
user.status = 'active'
user.package = productname
user.save!
end
I'd put the monkeypatch in an config/initializers/sassysimple.rb
Have you verified that the controller really sees all three params?
Is User#activate called anywhere else in the code? e.g. grep for it.
Related
I have a concern that checks addresses and zip codes with the intention of returning an error if the zip code does not match the state that is inputed. I also don't want the zip code to save unless the problem gets fixed.
The problem that I am having is that it appears that the if I submit the form, the error message in create pops up and I am not able to go through to the next page, but then somehow the default zip code is still saved. This only happens on edit. The validations are working on new.
I don't know if I need to share my controller, if I do let me know and I certainly will.
In my model I just have a
include StateMatchesZipCodeConcern
before_save :verify_zip_matches_state
Here is my concern
module StateMatchesZipCodeConcern
extend ActiveSupport::Concern
def verify_zip_matches_state
return unless zip.present? && state.present?
state_search_result = query_zip_code
unless state_search_result.nil?
return if state_search_result.upcase == state.upcase
return if validate_against_multi_state_zip_codes
end
errors[:base] << "Please verify the address you've submitted. The postal code #{zip.upcase} is not valid for the state of #{state.upcase}"
false
end
private
def query_zip_code
tries ||= 3
Geocoder.search(zip).map(&:state_code).keep_if { |x| Address::STATES.values.include?(x) }.first
rescue Geocoder::OverQueryLimitError, Timeout::Error
retry unless (tries -= 1).zero?
end
def validate_against_multi_state_zip_codes
::Address::MULTI_STATE_ZIP_CODES[zip].try(:include?, state)
end
end
I have a controller called blogger:
class BloggerController < ApplicationController
def home
end
def favoritePosts
#blogger = current_blogger
#favorites = #blogger.favorite_posts
end
def suggestedPosts
posts = Post.all
#suggestedPosts = posts.similar_posts
end
end
And in the Blogger model i have a method:
def similar_posts
title_keywords = Post.body.split(' ')
Post.all.sort do |post1, post2|
post1_title_intersection = post2.body.split(' ') & title_keywords
post2_title_intersection = post2.body.split(' ') & title_keywords
post2_title_intersection.length <=> post1_title_intersection.length
end
end
When i run the server its gives me an error:
undefined method `similar_posts' for #<Post::ActiveRecord_Relation:0x007fa365029760>
After searching on stackoverflow i tried def self.similar_postsbut it still gives the same error message. I also tried new in the controller like this #suggestedPosts = posts.new.similar_posts which still gives me the same error.
Any suggestions on how to overcome this?
You have 2 issues happening at the same time. The first is that you're using posts in your call, when you should be using something more like post.blogger. The specific object depends on what your intent actually is.
The second issue is that you're making the call to similar_posts on the association, not on an individual record. This can be resolved with a call to each on the association.
So, putting those together and looking at what you might have meant, I think that you might have intended this as your suggestedPosts method:
def suggestedPosts
posts = Post.all
#suggestedPosts = posts.map {|post| post.blogger.similar_posts }
end
I also changed the name of #suggestedDevelopers to #suggestedPosts, because I don't think that you meant 'developers' in this case. This should give you something closer to what it appear you were trying for.
I'm trying to monkey patch ActiveRecord::FinderMethods in order to use hashed ids for my models. So for example User.find(1) becomes User.find("FEW"). Sadly my overwritten method doesn't get called. Any ideas how to overwrite the find_one method?
module ActiveRecord
module FinderMethods
alias_method :orig_find_one, :find_one
def find_one(id)
if id.is_a?(String)
orig_find_one decrypt_id(id)
else
orig_find_one(id)
end
end
end
end
Here's an article that discusses how to actually do what you want by overriding the User.primary_key method like:
class User
self.primary_key = 'hashed_id'
end
Which would allow you to call User.find and pass it the "hashed_id":
http://ruby-journal.com/how-to-override-default-primary-key-id-in-rails/
So, it's possible.
That said, I would recommend against doing that, and instead using something like User.find_by_hashed_id. The only difference is that this method will return nil when a result is not found instead of throwing an ActiveRecord::RecordNotFound exception. You could throw this manually in your controller:
def show
#user = User.find_by_hashed_id(hashed_id)
raise ActiveRecord::RecordNotFound.new if #user.nil?
... continue processing ...
end
Finally, one other note to make this easier on you -- Rails also has a method you can override in your model, to_param, to tell it what property to use when generating routes. By default, of course, it users the id, but you would probably want to use the hashed_id.
class User
def to_param
self.hashed_id
end
end
Now, in your controller, params[:id] will contain the hashed_id instead of the id.
def show
#user = User.find_by_hashed_id(params[:id])
raise ActiveRecord::RecordNotFound.new if #user.nil?
... continue processing ...
end
I agree that you should be careful when doing this, but it is possible.
If you have a method decode_id that converts a hashed ID back to the original id, then the following will work:
In User.rb
# Extend AR find method to allow finding records by an encoded string id:
def self.find(*ids)
return super if ids.length > 1
# Note the short-circuiting || to fall-back to default behavior
find_by(id: decode_id(ids[0])) || super
end
Just make sure that decode_id returns nil if it's passed an invalid hash. This way you can find by Hashed ID and standard ID, so if you had a user with id 12345, then the following:
User.find(12345)
User.find("12345")
User.find(encode_id(12345))
Should all return the same user.
I want to add a slight extension to a class in a ruby gem.
The gem I am using is private_pub : https://github.com/ryanb/private_pub and the class I am overriding is : https://github.com/ryanb/private_pub/blob/master/lib/private_pub/faye_extension.rb
I am have placed my implementation in a new file in config/initializers called faye_extension.rb. The piece I have added to the code is ##clients and the get_list_of_subscribers class method. The code in faye_extension.rb is as follows:
PrivatePub::FayeExtension
module PrivatePub
# This class is an extension for the Faye::RackAdapter.
# It is used inside of PrivatePub.faye_app.
class FayeExtension
# Callback to handle incoming Faye messages. This authenticates both
# subscribe and publish calls.
##### MY NEW BIT
##clients = 0
def self.get_list_of_subscribers
##clients
end
####
def incoming(message, callback)
##clients = ##clients + 1
if message["channel"] == "/meta/subscribe"
authenticate_subscribe(message)
elsif message["channel"] !~ %r{^/meta/}
authenticate_publish(message)
end
callback.call(message)
end
private
# Ensure the subscription signature is correct and that it has not expired.
def authenticate_subscribe(message)
subscription = PrivatePub.subscription(:channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"])
if message["ext"]["private_pub_signature"] != subscription[:signature]
message["error"] = "Incorrect signature."
elsif PrivatePub.signature_expired? message["ext"]["private_pub_timestamp"].to_i
message["error"] = "Signature has expired."
end
end
# Ensures the secret token is correct before publishing.
def authenticate_publish(message)
if PrivatePub.config[:secret_token].nil?
raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly."
elsif message["ext"]["private_pub_token"] != PrivatePub.config[:secret_token]
message["error"] = "Incorrect token."
else
message["ext"]["private_pub_token"] = nil
end
end
end
end
When deployed into my application, the incoming() method of the FayeExtension code is called multiple times, however if in a view somewhere I use the following line of code:
<h3><%= PrivatePub::FayeExtension.get_list_of_subscribers.to_s %></h3>
to call my get_list_of_subscribers class method, it always returns 0, despite me calling incoming() 5 times, where I would expect it to output 5. So it seems my ##clients = ##clients +1 code inside of incoming() is not referencing or updating correctly my global variable.
How can I alter the code to achieve this?
##clients might be a class variable, but it is not shared between different processes. Applications deployed to production usually run multiple processes.
You will need to store that value somewhere where each process has access to: Your database, redis, memcache...
I am trying to create persistante variable local to my model but although something that sounds so simple is not working.
I have this in my model:
class Coupon < ActiveRecord::Base
#username = "empty"
#admin = false
def self.setUser(name, isAdmin)
#username = name
#admin = isAdmin
end
def self.get_user (user)#an attempt to access the current_user but did not work i call this from the controller (I understand it is not best practice)
##user = user
self.setUser(user.username,user.admin?)
end
def has_not_occurred
errors.add("property_of","name is not valid:#{#username}") if !validPropertyOf?
end
end
def validProperty_of?
return property_of == #username # || #Admin
end
end
I actually get a "" instead of "empty" or the new value of username in set.user. How do I make these values persist? I have printed the values inside each method so they persist inside the method but not beyond for some reason.
#username is always nil or "" when it gets to has_not_accurred.
Why is this and how do I make it persist? Thank you so much.
I cannont access #user when I set it either (get_user method). I get a nil instance later down at validateProperty_of
I think you forget about database table, you haven't create this one, that's why every variables are non-persistent.
Upgraded Rails now it works. Not sure why.