How should a Command Pattern inspired class be documented in YARD - ruby

While building a Project in Ruby I am required to document every important piece of code with RDoc, using YARD, the bulk of the work is done via Command Patterns Classes (from GOF).
The main structure of a Command Pattern is similar to this one:
class Command
def initialize(specific, params, for, command); end
def execute
# The specific code goes here
end
end
So the definition of the Command is basically the Class name, and instead of calling their execute methods with the specific parameters, you prepare them beforehand and then you just call execute.
So, does it make sense to document the initialize method or should the Class should be documented, here are my alternatives:
The "classic" YARD way (I'm in no way an expert in documentation)
# Generates a reservation for a user in an hotel room in certain date
class ReserveHotelRoom < Command
# Initializes the Use Case
# #param user [User] user which is making the reservation
# #param hotel [Hotel] hotel which is being booked
# #param room [String] room number/name
# #param date [Datetime] date for the reservation
def initialize(user:, hotel:, room:, date:); end
end
I think that a most "proper" way should be something like this, but kind of breaks YARD because I don't get the full support from its labels.
# Generates a Reservation for a user in an hotel room in a certain date
# #!attribute user [User]
# #!attribute hotel [Hotel] hotel which is being booked
# #!attribute room [String] room number/name
# #!attribute date [Datetime] date for the reservation
class ReserveHotelRoom < Command
# No documentation should be necessary inside because everything is 'standard'
end

Related

Documentation of Rails controllers using yard

When trying to create the documentation of my rails controller and the used parameters I'm getting stuck because yard seems to expect, that the parameters exists as method parameters.
# Renders a list of items
# #param params [Hash] the search parameters
# #option params [String] 'info' Only return items with this specific info
# #option params [Integer] 'limit' Only return <limit> items
def index
# ... do something smart here
end
So for this documentation yard will raise a warning and doesn't create the documentation:
[warn]: #param tag has unknown parameter name: params
in file `app/controllers/items_controller.rb' near line 8
Is there a way to document these kinds of items using yard or will I need to do this manually?
I am not aware of a Rails convention to document action parameters through Yard. Will be more than happy to learn about one if anyone out there knows more.
Yard is very flexible. You can for example define your own tags for the project. Add a tag dedicated to document your controller action parameters.
First include the tag definition in the Yard options file .yardopts. Th file is picked up from the project's root directory:
--type-name-tag controller_action_param:"Controller Action Parameters"
Use the new tag in your controller actions like in the example below:
# Renders a list of items
# #controller_action_param :info [String] Only return items with this specific info
# #controller_action_param :limit [Integer] Only return <limit> items
def index
# ... do something smart here
end
The convention you now defined is that the parameter name is the key used to access the params data - params[:info] for example.
Define a tag name and heading text that suits you most - this is just an example.

AES Decryption in ruby and activerecord

I have super ugly code that looks like this:
class User < ActiveRecord::Base
self.table_name = 'users'
def get_password
#test_password = User.find_by_sql "SELECT CAST(AES_DECRYPT(Pass, 'kkk') AS CHAR(50)) Pass From prod.sys_users Where Owner = '"+#owner+"' AND User = '"+#user+"'"
#test_password[0].Pass
end
end
This code works, but it makes me sick, since it is not written according to Ruby Coding Style. So I decided to fix this code, and here what I have so far:
class User < ActiveRecord::Base
self.table_name = 'users'
def get_pass
User.where(Owner: #owner, User: #user).pluck(:Pass).first
end
end
So, I am getting encrypted password, how can I decrypt it?
I tired OpenSSL, but key 'kkk' here is too short.
How can I resolve this issue?
In a situation like this, you might be better off converting the field values entirely. This could be done in a migration and once it's done, you never have to be concerned about how MySQL has stored the data. It's also one step toward database independence.
So, the migration would basically do 3 things:
add a flag column to track which records have been converted
iterate over each records, converting the encrypted value and setting the flag
remove the flag column once all records have been processed
The migration might look like this:
class ConvertMySqlEncryptedData < ActiveRecord::Migration
# Local proxy class to prevent interaction issues with the real User class
class User < ActiveRecord::Base
end
def up
# Check to see if the flag has already been created (indicates that migration may have failed midway through)
unless column_exists?(:users, :encrypted_field_converted)
# Add the flag field to the table
change_table :users do |t|
t.boolean :encrypted_field_converted, null: false, default: false
end
end
# Add an index to make the update step go much more quickly
add_index :users, :encrypted_field_converted, unique: false
# Make sure that ActiveRecord can see the new column
User.reset_column_information
# Setup for AES 256 bit cipher-block chaining symetric encryption
alg = "AES-256-CBC"
digest = Digest::SHA256.new
digest.update("symetric key")
key = digest.digest
iv = OpenSSL::Cipher::Cipher.new(alg).random_iv
key64 = Base64.encode(key)
# Don't update timestamps
ActiveRecord::Base.record_timestamps = false
begin
# Cycle through the users that haven't yet been updated
User.where(encrypted_field_converted: false).pluck("CAST(AES_DECRYPT(Pass, 'kkk') AS CHAR(50)) Pass").each do |user|
# Re-encode the password with OpenSSL AES, based on the setup above
new_pass = aes.update(user.pass).final
# Update the password on the row, and set the flag to indicate that conversion has occurred
user.update_attributes(pass: new_pass, encrypted_field_converted: true)
end
ensure
# Reset timestamp recording
ActiveRecord::Base.record_timestamps = true
end
end
def down
# To undo or not undo, that is the question...
end
end
This was off the top of my head, so there may be issues with the encryption. Structure-wise, it should be in good shape, and it takes into account a number of things:
Provides incremental database processing by using a flag to indicate progress
Uses an index on the flag field to improve query performance, particularly if multiple runs are required to complete processing
Avoids updating the updated_at column to prevent overwriting prior values that may be useful to keep (this is not a material change, so updated_at doesn't require updating)
Plucks only the pass field, so that transfer overhead is minimized
Now, you can query pass and encrypt/decrypt as needed by the application. You can document and support the field at the application level, rather than rely on the database implementation.
I spent a few years consulting and doing database conversion, either from one database product to another, or as part of a significant version upgrade. It also allows development to use lighter-weight databases (e.g. SQLite) or test viability with other products when upscaling is needed. Avoiding database-specific features, like the MySQL encryption, will save you (or your employer) a LOT of money and hassle in the long run. Database independence is your friend; embrace it and use what ActiveRecord provides to you.

DataMapper: Fetching only changed properties before updating record

Hoping someone can point me in the right direction with this.
I want to know if there's a way to view/fetch both new and old property values with DataMapper before the update method is called and compare the values.
The scenario is as follows:
I have a ticket resource and I need to notify various interested parties about changes made to the ticket. Email notification when the payment status changes, SMS notification when the ticket get's assigned to a support staff etc.
Currently, inside my Ticket class, I have set up a callback/filter like this:
before :update, :notify_changes
def notify_changes
ticket = Ticket.get(self.id) # Get the original
if ticket.status != self.status
# Send out the email notification
end
if ticket.assigned_support != self.assigned_support
# Send out the SMS notification
end
# ... etc
end
Is there a better or more efficient way to do this without hitting the database again at ticket = Ticket.get(self.id)?
Ok, I've figured this out myself. Here it is for reference if anyone else finds themselves asking the same question:
before :update, :notify_changes
def notify_changes
# The status property has been changed
if !dirty_attributes[Ticket.properties[:status]].nil?
# old status: original_attributes[Ticket.properties[:status]]
end
# The assigned_support property has been changed
if !dirty_attributes[Ticket.properties[:assigned_support]].nil?
# old status: original_attributes[Ticket.properties[:assigned_support]]
end
end
Inspiration Reference: This thread
Yes, I was referring to dirty when I asked that. Just to add a little more incase someone else comes across this question.
There are a few methods one can call to check the status of an attribute or model object.
- (Boolean) attribute_dirty?(name)
- (Boolean) clean?
- (Boolean) dirty?
- (Hash) dirty_attributes # your choice
- (Hash) original_attributes
These are part of DataMapper::Resource and can be found here:
http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/Resource

Subclass Rack::Throttle

i am reading about Rack::Throttle and i want to change the default client identifier from an IP to somethng else. The documentation says it can be done
The rate-limiting counters stored and maintained by Rack::Throttle are
keyed to unique HTTP clients.
By default, HTTP clients are uniquely identified by their IP address
as returned by Rack::Request#ip. If you wish to instead use a more
granular, application-specific identifier such as a session key or a
user account name, you need only subclass a throttling strategy
implementation and override the #client_identifier method.
I have no clue where to add that in, here is my current subclass for another method. Does anybody know how to do this? https://github.com/datagraph/rack-throttle
module Rack
module Throttle
class DailyRequests < Daily
def allowed?(request)
## Insert rules
super request
end
end
class HourlyRequests < Hourly
def allowed?(request)
## Insert rules
super request
end
end
class RequestInterval < Interval
def allowed?(request)
## Insert rules
super request
end
end
end
end
You should subclass one of the existing rack-throttle classes (probably either Rack::Throttle::Interval or Rack::Throttle::TimeWindow, whichever one more closely aligns with your needs) and override the #client_identifier method.
#client_identifier is passed one argument, request, which is a Rack::Request instance containing information passed in the incoming HTTP request and can be used to get information such as HTTP headers, cookies, path, and possibly other info depending on your app. The default implementation looks like this:
# #param [Rack::Request] request
# #return [String]
def client_identifier(request)
request.ip.to_s
end
Here's an example of subclassing Rack::Throttle::Interval to match requests on a query parameter such as ?user_id=<id>:
class UserThrottle < Rack::Throttle::Interval
def client_identifier(request)
request['user_id']
end
end
which you could use in a Rack application with:
use UserThrottle, :min => 100
Notice you can still pass options like :min to the Rack use statement, since it is just subclassing the existing throttle classes. And adopting this in a Rails app would just involve calling use in your application.rb file (see Rails on Rack).

Devise: Is it possible to NOT send a confirmation email in specific cases ? (even when confirmable is active)

Here is my situation, I use devise to allow users to create account on
my site and manage their authentication.
During the registration process I allow customers to change some
options, leading to an actually different account being created but
still based on the same core user resource.
I would like to choose not to send a confirmation email for some of
those account types. I don't care if the account do not get confirmed
and user cannot log in, that's ok, no pb with that.
How would I go about doing that ?
Thanks,
Alex
Actually it's quite easy once I dig a little deeper.
Just override one method in your User model (or whatever you are using):
# Callback to overwrite if confirmation is required or not.
def confirmation_required?
!confirmed?
end
Put your conditions and job's done !
Alex
If you just want to skip sending the email but not doing confirmation, use:
# Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
# #skip_confirmation!, record still requires confirmation.
#user.skip_confirmation_notification!
If you don't want to call this in your model with a callback overwrite this method:
def send_confirmation_notification?
false
end
You can also simply add the following line of code in your controller before creating the new user:
#user.skip_confirmation!
I don't know if Devise added this after the other answers were submitted, but the code for this is right there in confirmable.rb:
# If you don't want confirmation to be sent on create, neither a code
# to be generated, call skip_confirmation!
def skip_confirmation!
self.confirmed_at = Time.now
end
I was able to do something similar with the functions:
registrations_controller.rb
def build_resource(*args)
super
if session[:omniauth] # TODO -- what about the case where they have a session, but are not logged in?
#user.apply_omniauth(session[:omniauth])
#user.mark_as_confirmed # we don't need to confirm the account if they are using external authentication
# #user.valid?
end
end
And then in my user model:
user.rb
def mark_as_confirmed
self.confirmation_token = nil
self.confirmed_at = Time.now
end

Resources