1

This is my current Rails file structure:

hello-app
  app
    assets
    controller
    helpers
    models
      application_record.rb
      short_link.rb
      obfuscate.rb

PROBLEM: The obfuscate.rb file is located next to the short_link.rb file where it doesn't belong.

GOAL: I want to move the obfuscate.rb file to a different location, see new structure below:

hello-app
  app
    assets
    controller
    helpers
    models
      application_record.rb
      short_link.rb
    services
      models
        obfuscate.rb

And import the obfuscate.rb module into the short_link.rb model.

Here is my current short_link.rb model file:

require 'obfuscate'

class ShortLink < ApplicationRecord
  include Obfuscate

  def to_param
    encrypt(id)
  end
end

And my obfuscate.rb file:

require 'openssl'
require 'base64'

module Obfuscate
  def self.included(base)
    base.extend self
  end

  def cipher
    OpenSSL::Cipher::Cipher.new('aes-256-cbc')
  end

  def cipher_key
    'custom_cipher_key'
  end

  def decrypt(value)
    c = cipher.decrypt
    c.key = Digest::SHA256.digest(cipher_key)
    c.update(Base64.urlsafe_decode64(value.to_s)) + c.final
  end

  def encrypt(value)
    c = cipher.encrypt
    c.key = Digest::SHA256.digest(cipher_key)
    Base64.urlsafe_encode64(c.update(value.to_s) + c.final)
  end
end

Also, is it a good practice to move the obfuscate.rb file to hello-app>>app>>services>>models>>obfuscate.rb? Or would there be a better location for this extra logic?

Anthony
  • 931
  • 1
  • 13
  • 30
  • 1
    In Rails conventions you would place mixins that are shared among models in `/app/models/concerns/`. – max Jul 14 '19 at 20:50

2 Answers2

3

If your class doesn't fit neatly into any of the pre-defined locations within app/ you can just make your own. Common names are lib/ or concerns/ depending on your preference, at least in Rails 5+ where this behaviour was introduced. Prior to this you needed to manually add paths in your application configuration.

In this case, app/lib/obfuscate.rb will be auto-loaded as Obfuscate, which is what you want.

In general terms app/*/x.rb auto-loads as X for any value in the middle. The intermediate path name is basically irrelevant and is only used for organization and grouping.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 1
    I would say that using `app/lib` is an anti-pattern. The app directory is for application code - `/lib` is for project code that is not quite application code. Using `/app/models/concerns` is far more appropriate. – max Jul 14 '19 at 20:55
  • 1
    @max If it's part of the application, it's application code. The nice thing about `/app` is it will auto-reload, anything out side of that, with the exception of `config/routes.rb`, requires a manual stop/start of the server. This can make iterating on code outside of `/app` a lot more tedious. You can call the directory whatever you want. `/app/models/concerns` should be for things that relate to models, not used as a dumping ground for non-model code. – tadman Jul 14 '19 at 21:13
0

you can add subfolder to autoload with this way

open config/application.rb and add folder path to autoload_paths

class Application < Rails::Application
  ...
  config.autoload_paths += [
      "#{Rails.root}/app/services/models",
      "#{Rails.root}/app/services/other_folder"
    ]
  end
end

rails will autoload all module files within that folders

widjajayd
  • 6,090
  • 4
  • 30
  • 41