Ruby/Rails, Gate clause syntax to enforce security policy - ruby-on-rails

I have a difficult problem to solve.
I would like to replicate, the title_with_if_statement methods results, but using the syntax used in the title method. However, I do not know what to put in the enforce_policy_to_level() method to accomplish this.
I can do 'title_less_readable', but I find the title method much more readable since the policy is right at the top, and since I will be doing this for hundreds of methods, I want to keep it DRY and readable.
Any advice?
Is this even possible? It's basically a before_action but uses the method's own results.
class Car
def title
enforce_policy_to_level(1)
# Code that returns the "Car Title" string
"Car Title"
end
def title_less_readable
# Code that returns "Car Title" string
content = "Car Title"
return secure_text(content) unless authorized_to_level?(1)
content
end
def title_with_if_statement
# Code that returns the "Car Title" string
content = "Car Title"
if authorized_to_level?(1)
content
else
secure_text(content)
end
end
private
def enforce_policy_to_level(level)
# Return from title method with the secured result of title method
# if it's not authorized. In this example it would be secure_text('Car Title')
# which output return '---------'
# If if authorized, just continue with how title method would normally return
end
def authorized_to_level?(level)
current_user_level = 1 # Dynamic from user record
current_user_level >= level
end
def secure_text(text)
'X' * text.length
end
end

Why not just something like this?
def title
enforce_policy_to_level(1) do
"Car Title"
end
end
private
def enforce_policy_to_level(level, &block)
if authorized_to_level?(level)
secure_txt yield
else
yield
end
end

Consider separating your concerns: I'd approach this by creating a separate function that handles the authorization.
module AuthorizedText
module_function
def secure_text(text, authorized)
return authorized ? text : 'X' * text.length
end
end
Call this in Car like this:
AuthorizedText.secure_text("Car Title", authorized_to_level(1))
You can shorthand this with a convenience method:
def secure(level, text)
AuthorizedText.secure_text(text, authorized_to_level(level))
end
so that your car_title method would look like
def car_title
secure(1, "Car Title")
end
You could generalize a method wrapper by defining a class method that would let you do something like
def car_title
"Car Title"
end
secure :car_title
But I'd call this over-engineered and ultimately less readable to anyone who isn't you (including you 8 months from now, likely!). I'd avoid trying to be fancy like this, as fun as it may be.
Also, if current_user and current_user_level make sense outside the context of Car and you may want to reuse this elsewhere, you could go further by moving the auth checking methods into AuthorizedText, if that's appropriate:
module AuthorizedText
module_function
def secure_text(text, user, level = 0)
return authorized?(user, level) ? text : 'X' * text.length
end
def authorized?(user, level)
# for example; I don't know what your user object looks like
user.level >= level
end
end

Related

RSpec "change all" matcher

I've got some job that updates records, and I want something like:
it 'updates each record' do
expect {
described_class.perform_now
}.to(change_all_of{
Record.pluck(:updated_at)
})
end
Only I can't find anything that looks like how to accomplish this, or what I can recognize as the docs for how to write a custom matcher.
The issue is that change, on an array, will return true if any element has changed; I only want to return true if every element has changed.
Can anyone point me either at whatever I missed that would let me do this, OR, whatever docs/info I need to write my own change matcher?
Alright, thanks to this answer on another question, and staring at the actual code, here's what I've got -
module RSpec
module Matchers
def change_all &block
BuiltIn::ChangeAll.new(nil, nil, &block)
end
module BuiltIn
class ChangeAll < Change
def initialize receiver=nil, message=nil, &block
#change_details = ChangeAllDetails.new(receiver, message, &block)
end
def failure_message
"expected all elements to change, but " + (
#change_details.actual_after & #change_details.actual_before
).collect do |unchanged|
"before[#{#change_details.actual_before.find_index(unchanged)}] " \
"after[#{#change_details.actual_after.find_index(unchanged)}] " \
"#{unchanged}"
end.join(",") + " remained the same"
end
end
class ChangeAllDetails < ChangeDetails
attr_accessor :actual_before
def changed?
!(#actual_after & #actual_before).any?
end
end
end
end
end
Suggestions welcome!

How to decouple functionality and logging in a ruby method

I like to log a lot. In my Rails app I have a lot of methods like:
def my_method(argument1:, argument2:)
logger.info "Starting my_method with arguments: #{argument1} and #{argument2}"
result = argument1 + argument2
logger.info "Finished my_method with result: #{result}"
end
How to decouple the functionality and the logging of the methods?.
Ideally the result would look something like this (borrowing the callback concept from Rails just as an example):
before_method: :my_method_log_start, only: :my_method
after_method: :my_method_log_end, only: :my_method
def my_method(argument1:, argument2:)
result = argument1 + argument2
end
private
def my_method_log_start
logger.info "Starting my_method with arguments: #{argument1} and #{argument2}"
end
def my_method_log_end
logger.info "Finished my_method with result: #{result}"
end
I know this is less efficient in terms of lines of code, it is more readable (in my opinion).
I have read about Aspect Orient Programming and some of their gems like Aquarius, but looks like an overkill to add a new paradigm just for logging.
I think Avdi Grimm has a good explanation of the technique you could use. The idea is to extract logging (or anything else) to the listener class and publish events to that listener, basic example would be
class Task
# ...
def add_listener(listener)
(#listeners ||= []) << listener
end
# ...
def notify_listeners(event_name, *args)
#listeners && #listeners.each do |listener|
if listener.respond_to?(event_name)
listener.public_send(event_name, self, *args)
end
end
end
end
and do sth like
task = Task.new
task.add_lestener(YourLoggerClass.new)
task.notify_listeners(:start_logging)
task.notify_listeners(:end_logging)
If this is only for local debugging, it is the good use case for TracePoint class. Here is the code:
tp1 = TracePoint.new do |tp|
if tp.event == :call
method = tp.defined_class.method(tp.method_id)
arguments = method.parameters.map do |param|
"#{param[1]}: #{tp.binding.local_variable_get(param[1])}"
end.join(", ")
puts "Starting #{tp.method_id} with arguments #{arguments}"
elsif tp.event.to_s == "return"
puts "Finished #{tp.method_id} with result: #{tp.return_value}"
end
end
tp1.enable
def my_method1(a, b)
a + b
end
puts my_method1(2, 3)
I recommend reading the documentation for this class, it has really nice features. Of course you need to polish this code a little bit to handle some edge cases. You can add some filter to only invoke tracing block for methods that you care about. Or you can enable/disable this based on some parts of the code.
You can call method by it's name, or turn it to proc and pass to another method. So you can write something like that:
def foo(a, b)
a + b
end
def call_with_logging(method_name, *args)
args_as_string = args.map(&:to_s).join(' ')
puts "Starting my_method with arguments #{args_as_string}"
result = Object.send(method_name, *args)
puts "Finished my_method with result: #{result}"
end
call_with_logging :foo, 1, 2

Rails 5 - iterate until field matches regex

In my app that I am building to learn Rails and Ruby, I have below iteration/loop which is not functioning as it should.
What am I trying to achieve?
I am trying to find the business partner (within only the active once (uses a scope)) where the value of the field business_partner.bank_account is contained in the field self_extracted_data and then set the business partner found as self.sender (self here is a Document).
So once a match is found, I want to end the loop. A case exists where no match is found and sender = nil so a user needs to set it manually.
What happens now, is that on which ever record of the object I save (it is called as a callback before_save), it uses the last identified business partner as sender and the method does not execute again.
Current code:
def set_sender
BusinessPartner.active.where.not(id: self.receiver_id).each do |business_partner|
bp_bank_account = business_partner.bank_account.gsub(/\s+/, '')
rgx = /(?<!\w)(#{Regexp.escape(bp_bank_account)})?(?!\‌​w)/
if self.extracted_data.gsub(/\s+/, '') =~ rgx
self.sender = business_partner
else
self.sender = nil
end
end
end
Thanks for helping me understand how to do this kind of case.
p.s. have the pickaxe book here yet this is so much that some help / guidance would be great. The regex works.
Using feedback from #moveson, this code works:
def match_with_extracted_data?(rgx_to_match)
extracted_data.gsub(/\s+/, '') =~ rgx_to_match
end
def set_sender
self.sender_id = matching_business_partner.try(:id) #unless self.sender.id.present? # Returns nil if no matching_business_partner exists
end
def matching_business_partner
BusinessPartner.active.excluding_receiver(receiver_id).find { |business_partner| sender_matches?(business_partner) }
end
def sender_matches?(business_partner)
rgx_registrations = /(#{Regexp.escape(business_partner.bank_account.gsub(/\s+/, ''))})|(#{Regexp.escape(business_partner.registration.gsub(/\s+/, ''))})|(#{Regexp.escape(business_partner.vat_id.gsub(/\s+/, ''))})/
match_with_extracted_data?(rgx_registrations)
end
In Ruby you generally want to avoid loops and #each and long, procedural methods in favor of Enumerable iterators like #map, #find, and #select, and short, descriptive methods that each do a single job. Without knowing more about your project I can't be sure exactly what will work, but I think you want something like this:
# /models/document.rb
class Document < ActiveRecord::Base
def set_sender
self.sender = matching_business_partner.try(:id) || BusinessPartner.active.default.id
end
def matching_business_partners
other_business_partners.select { |business_partner| account_matches?(business_partner) }
end
def matching_business_partner
matching_business_partners.first
end
def other_business_partners
BusinessPartner.excluding_receiver_id(receiver_id)
end
def account_matches?(business_partner)
rgx = /(?<!\w)(#{Regexp.escape(business_partner.stripped_bank_account)})?(?!\‌​w)/
data_matches_bank_account?(rgx)
end
def data_matches_bank_account?(rgx)
extracted_data.gsub(/\s+/, '') =~ rgx
end
end
# /models/business_partner.rb
class BusinessPartner < ActiveRecord::Base
scope :excluding_receiver_id, -> (receiver_id) { where.not(id: receiver_id) }
def stripped_bank_account
bank_account.gsub(/\s+/, '')
end
end
Note that I am assigning an integer id, rather than an ActiveRecord object, to self.sender. I think that's what you want.
I didn't try to mess with the database relations here, but it does seem like Document could include a belongs_to :business_partner, which would give you the benefit of Rails methods to help you find one from the other.
EDIT: Added Document#matching_business_partners method and changed Document#set_sender method to return nil if no matching_business_partner exists.
EDIT: Added BusinessPartner.active.default.id as the return value if no matching_business_partner exists.

a method that changes the result of a setter

I am assigned to write some ruby code that will work with the following (segment of a) rspec test:
before do
#book = Book.new
end
describe 'title' do
it 'should capitalize the first letter' do
#book.title = "inferno"
#book.title.should == "Inferno"
end
This is the solution, but I don't understand it:
class Book
attr_reader :title
def title=(new_title)
words = new_title.split(" ")
words = [words[0].capitalize] +
words[1..-1].map do |word|
little_words = %w{a an and the in of}
if little_words.include? word
word
else
word.capitalize
end
end
#title = words.join(" ")
end
end
I think I am correct to deduce that #book.title = "inferno" will run the title method and eventually create a new value for the #title variable at the bottom. I know that this causes #book.title to update to "Inferno" (capitalized), but I'm not sure why. Is this a case of def title being some sort of variable method, and #title being it's final value? That's my best guess at this point.
EDIT in case it's not clear, what I'm not understanding is why setting #book.title ='inferno' causes #book.title to update to "Inferno".
When you have setter and getter methods in Ruby:
attr_writer :something
attr_reader :something
From my little understanding of this, these methods are equivalent to
def something=(value)
#something = value
end
def something
#something
end
Respectively.
Or in one statement, it could be:
attr_accessor :something
Anyway, what you are doing is to write the setter method yourself, capitalising each word of the string passed as an argument.
Your understanding is almost correct. Here is a simple example
class Chapter
attr_reader :title
def title=(new_title)
#title = new_title.reverse
end
end
#c = Chapter.new
#c.title = "ybuR"
#c.title #=> Ruby

Ruby on Rails: Execute Logic Based on Selected Menu

I have a class that I use to contain select menu options for property types. It works fine. However, I need to be able to verify the selection and perform specific logic based on the selected option. This needs to happen in my Ruby code and in JavaScript.
Here is the class in question:
class PropertyTypes
def self.[](id)
##types[id]
end
def self.options_for_select
##for_select
end
private
##types = {
1 => "Residential",
2 => "Commercial",
3 => "Land",
4 => "Multi-Family",
5 => "Retail",
6 => "Shopping Center",
7 => "Industrial",
8 => "Self Storage",
9 => "Office",
10 => "Hospitality"
}
##for_select = ##types.each_pair.map{|id, display_name| [display_name, id]}
end
What is the best way to verify the selection? I need to perform specific logic and display user interface elements based on each type of property type.
Since I am storing the id, I would be verifying that the id is a particular property type. Something like:
PropertyTypes.isResidential?(id)
Then this method would look like this:
def self.isResidential?(id)
##types[id] == "Residential"
end
But now I am duplicating the string "Residential".
For JavaScript, I assume I would make an ajax call back to the model to keep the verification code DRY, but this seems like over kill.
Do I need to manually create a verification method for each property type or can I use define_method?
This seems so basic yet I am confused and burned out on this problem.
Thanks
===
Here's my solution:
class << self
##types.values.each do |v|
# need to remove any spaces or hashes from the found property type
v = v.downcase().gsub(/\W+/, '')
define_method "is_#{v}?", do |i|
type_name = ##types[i]
return false if type_name == nil #in case a bogus index is passed in
type_name = type_name.downcase().gsub(/\W+/, '')
type_name == v
end
end
end
It sounds like you can benefit from some Ruby meta-programming. Try googling "ruby method_missing". You can probably do something quick & dirty along the lines of:
class PropertyTypes
def method_missing(meth, *args, &block)
if meth.to_s =~ /^is_(.+)\?$/
##types[args.first] == $1
else
super
end
end
end
On the ruby side you could also use something like this to define dynamically these methods:
class << self
##types.values.each do |v|
define_method "is_#{v}?", do |i|
##types[i] == v
end
end
end

Resources