Create encode and decode methods in rails? - ruby-on-rails

I'm using the base62 gem for obfuscation/shortening ids. So I have things like:
552.base62_encode
=> "8u"
"8u".base62_decode
=> 552
I'd like to alias these in an initializer so they're cleaner, like so:
class Fixnum
def encode
base62_encode
end
end
class String
def decode
base62_decode
end
end
Are there any issues with using the words encode and decode directly? Will this cause problems with any interdependencies, other gems, etc, or is this safe to do?

Well, since the String#encode method is defined by ruby, yes, it will cause problems.
As a general note, you should run away from monkey patching as fast as you can.
It will only make your applications harder to comprehend for new developers, and harder to maintain.

Related

Rails def initialize slug explained

I am trying to learn Rails and one of the files that I downloaded I can see this:
NOTE: this file is under models.
def initialize slug
api_call "http://yourwebsiteurl.com/REST/" + slug.to_s
end
What does this mean? is it like a constructor in Java?
Youssef
As answered in the comments, the initiailze method is Ruby's new object constructor method. That is, when you call new on an object it turns around and calls initialize.
But, since you're new to Ruby I'd like to also point out the stylistic issues with the code. The idiomatic Ruby way to write this method would be:
def initialize(slug)
api_call "http://yourwebsiteurl.com/REST/#{slug}"
end
The differences being:
Always use parentheses around method arguments
Always use 2 spaces for indentation
Prefer String interpolation ("#{}") over String concatenation (+) to insert variables because it's easier and more performant
While these are somewhat arbitrary differences they're still fairly widely accepted. See the GitHub Ruby Style Guide.

How to create a deep copy of an object in Ruby?

I did some searching found some different methods and posts about creating a deep copy operator.
Is there a quick and easy (built-in) way to deep copy objects in Ruby? The fields are not arrays or hashes.
Working in Ruby 1.9.2.
Deep copy isn't built into vanilla Ruby, but you can hack it by marshalling and unmarshalling the object:
Marshal.load(Marshal.dump(#object))
This isn't perfect though, and won't work for all objects. A more robust method:
class Object
def deep_clone
return #deep_cloning_obj if #deep_cloning
#deep_cloning_obj = clone
#deep_cloning_obj.instance_variables.each do |var|
val = #deep_cloning_obj.instance_variable_get(var)
begin
#deep_cloning = true
val = val.deep_clone
rescue TypeError
next
ensure
#deep_cloning = false
end
#deep_cloning_obj.instance_variable_set(var, val)
end
deep_cloning_obj = #deep_cloning_obj
#deep_cloning_obj = nil
deep_cloning_obj
end
end
Source:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43424
I've created a native implementation to perform deep clones of ruby objects.
It's approximately 6 to 7 times faster than the Marshal approach.
https://github.com/balmma/ruby-deepclone
Note that this project is not maintained anymore (last commit in 2017, there are reported issues)
Rails has a recursive method named deep_dup that will return a deep copy of an object and, on the contrary of dup and clone, works even on composite objects (array/hash of arrays/hashes).
It's as easy as:
def deep_dup
map { |it| it.deep_dup }
end
There is a native implementation to perform deep clones of ruby objects: ruby_deep_clone
Install it with gem:
gem install ruby_deep_clone
Example usage:
require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)
It's approximately 6 to 7 times faster than the Marshal approach and event works with frozen objects.
Note that this project is not maintained anymore (last commit in 2017, there are reported issues)
You can use duplicate gem for this.
It's a small pure ruby gem that able to recursively duplicate object
It will duplicate it's object references too to the new duplication.
require 'duplicate'
duplicate('target object')
https://rubygems.org/gems/duplicate
https://github.com/adamluzsi/duplicate.rb
Automatic deep clone is not always what you want. Often you need to define a selected few attributes to deep clone. A flexible way to do this is to implement the initialize_copy, initialize_dup and initialize_clone methods.
If you have a class:
class Foo
attr_accessor :a, :b
end
and you only want to only deep clone :b, you override the initialize_* method:
class Foo
attr_accessor :a, :b
def initialize_dup(source)
#b = #b.dup
super
end
end
Of course if you want #b to also deep clone some of its own attributes, you do the same in b's class.
Rails does this (see https://github.com/rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78)
For more complete explanation, I learned it here from this post https://aaronlasseigne.com/2014/07/20/know-ruby-clone-and-dup/
I would suggest you use the ActiveSupport gem which adds a lot of sugar to your native Ruby core, not just a deep clone method.
You can look into the documentation for more information regarding the methods that have been added.
You really don't need a Gem for this. This couldn't be much simpler than this, which is not worth the overhead of a Gem!
def deep_clone(obj)
obj.clone.tap do |new_obj|
new_obj.each do |key, val|
new_obj[key] = deep_clone(val) if val.is_a?(Hash)
end
end
end
Also check out deep_dive. This allows you to do controlled deep copies of your object graphs.
https://rubygems.org/gems/deep_dive

What is the best way to obfuscate numerical IDs in an application

Given I've got a site where most of the resources have numerical IDs (i.e. user.id question.id etc.) but that like the Germans looking back on WWII I'd rather not reveal these to the observers, what's the best way to obfuscate them?
I presume the method is going to involve the .to_param and then some symmetric encryption algorithm but I'm not sure what's the most efficient encryption to do and how it'll impact lookup times in the DB etc.
Any advice from the road trodden would be much appreciated.
I published a Rails plugin that does this called obfuscate_id. I didn't need it to be secure, but just to make the id in the url non-obvious to the casual user. I also wanted it to look cleaner than a long hash.
It also has the advantage of needing no migrations or database changes. It's pretty simple.
Just add the gem to your Gemfile:
gem 'obfuscate_id'
And add call the obfuscate id in your model:
class Post < ActiveRecord::Base
obfuscate_id
end
This will create urls like this:
# post 7000
http://example.com/posts/5270192353
# post 7001
http://example.com/posts/7107163820
# post 7002
http://example.com/posts/3296163828
You also don't need to look up the records in any special way, ActiveRecord find just works.
Post.find(params[:id])
More information here:
https://github.com/namick/obfuscate_id
I usually use a salted Hash and store it in the DB in an indexed field. It depends on the level of security you expect, but I use one salt for all.
This method makes the creation a bit more expensive, because you are going to have an INSERT and an UPDATE, but your lookups will be quite fast.
Pseudo code:
class MyModel << ActiveRecord::Base
MY_SALT = 'some secret string'
after_create :generate_hashed_id
def to_param
self.hashed_id
end
def generate_hashed_id
self.update_attributes(:hashed_id => Digest::SHA1.hexdigest("--#{MY_SALT}--#{self.id}--"))
end
end
Now you can look up the record with MyModel.find_by_hashed_id(params[:id]) without any performance repercussions.
Here's a solution. It's the same concept as Wukerplank's answer, but there's a couple of important differences.
1) There's no need to insert the record then update it. Just set the uuid before inserting by using the before_create callback. Also note the set_uuid callback is private.
2) There's a handy library called SecureRandom. Use it! I like to use uuid's, but SecureRandom can generate other types of random numbers as well.
3) To find the record use User.find_by_uuid!(params[:id]). Notice the "!". That will raise an error if the record is not found just like User.find(params[:id]) would.
class User
before_create :set_uuid
def to_param
uuid
end
private
def set_uuid
self.uuid = SecureRandom.uuid
end
end
Hashids is a great cross-platform option.
You can try using this gem,
https://github.com/wbasmayor/masked_id
it obfuscates your id and at the same time giving each model it's own obfuscated code so all no. 1 id won't have the same hash. Also, it does not override anything on the rails side, it just provides new method so it doesn't mess up your rails if your also extending them.
Faced with a similar problem, I created a gem to handle the obfuscation of Model ids using Blowfish. This allows the creation of nice 11 character obfuscated ids on the fly. The caveat is, the id must be within 99,999,999, e.g. a max length of 8.
https://github.com/mguymon/obfuscate
To use with Rails, create an initializer in config/initializers with:
require 'obfuscate/obfuscatable'
Obfuscate.setup do |config|
config.salt = "A weak salt ..."
end
Now add to models that you want to be Obfuscatable:
class Message < ActiveRecord::Base
obfuscatable # a hash of config overrides can be passed.
end
To get the 11 character obfuscated_id, which uses the Blowfish single block encryption:
message = Message.find(1)
obfuscated = message.obfuscated_id # "NuwhZTtHnko"
clarified = message.clarify_id( obfuscated ) # "1"
Message.find_by_obfuscated_id( obfuscated )
Or obfuscate a block of text using Blowfish string encryption, allowing longer blocks of text to be obfuscated:
obfuscated = message.obfuscate( "if you use your imagination, this is a long block of text" ) # "GoxjVCCuBQgaLvttm7mXNEN9U6A_xxBjM3CYWBrsWs640PVXmkuypo7S8rBHEv_z1jP3hhFqQzlI9L1s2DTQ6FYZwfop-xlA"
clarified = message.clarify( obfuscated ) # "if you use your imagination, this is a long block of text"

Is there any built-in way to automatically enforce a type/class on an instance variable in Ruby?

I'm working with Ruby and Rails, so any Rails extension of Ruby should be fine too.
I'm wondering if there's a way to effectively force a type on instance variables (rather, their setters and getters) that's easier than manually defining them.
The class method attr_accessor and the like don't enforce a type. I noticed for instance that Rails' ActiveRecord::Base class does automatic casting on setters. It knows from the database that a particular active record variable is an integer, and setting it with #record.integer_variable = '1' automatically casts the argument and allows further access through #record.integer_variable # => 1.
Is there a way to tap into this?
I could write my own getters and setters as class methods, but if smarter folk have already done the heavy lifting, I'd rather not have to trust myself.
I don't know if there's already something about it, but you can solve this problem with just a few lines of meta-programming:
module EnforceTypes
def attr_accessor_of_type(name, type)
send :define_method, name do
instance_variable_get("##{name}")
end
send :define_method, "#{name}=" do |v|
raise ArgumentException unless v.is_a? type
instance_variable_set("##{name}", v)
end
end
end
Usage:
class MyClass
extend EnforceTypes
attr_accessor_of_type :some_string, String
end
Of course you can make it a little smart by changing the 2nd emitted method, performing some conversions, etc.
Here's a nice reference: http://www.raulparolari.com/Ruby2/attr_accessor
And remember, almost anything that you can do by manually copy-and-pasting lots of code, can be solved with meta-programming.

Best way to encode URLs?

I am currently developing a CMS and want to encode special chars in the URL in a nice way.
I don't want to use Rack::Utils.escape.
Is there already a cool gem available?
Best regards
Look at the stringex gem here, it can be used even without rails, but contains some stuff to make it easier to use(with rails).
Ruby's CGI library should do what you need:
url_encoded_string = CGI::escape("'Stop!' said Fred")
# => "%27Stop%21%27+said+Fred"
See http://ruby-doc.org/core/classes/CGI.html
Well, I normally use a handy custom-made method called String.to_slug. I hope you find it useful.
Call this /lib/to_slug.rb and include it in one initializer, or include it only on the model that generates the urls.
String.class_eval do
#converts accented letters into ascii equivalents (eg. ñ becomes n)
def normalize
#this version is in the forums but didn't work for me
#chars.normalize(:kd).gsub!(/[^\x00-\x7F]/n,'').to_s
mb_chars.normalize(:d).gsub(/[^\x00-\x7F]/n,'').to_s
end
#returns an array of strings containing the words on a string
def words
gsub(/\W/, ' ').split
end
#convert into a nice url-ish string
def to_slug(separator='-')
strip.downcase.normalize.words.join(separator)
end
end

Resources