Trinity As Seen By A Programmer

Any thoughts on the following: Trinity As Seen By A Programmer?


class God
  # John 1:3
  def can_know_everything?
    true
  end

  # Matthew 19:26
  def can_do_everything?
    true
  end

  # Isaiah 55:8
  def is_like_a_human?
    false
  end
end

class Father < God
end

class Son < God
  # Mark 13:32
  def can_know_everything?
    false
  end

  # Hebrews 2:18
  def is_like_a_human?
    true
  end
end

class HolySpirit < God
end

TeleSMS gem released

The TeleSMS gem was recently released. This gem helps with sending and receiving SMS messages using emails. For example, Verizon has an email-to-sms gateway that allows users to send SMS messages by sending an email to NUMBER@vtext.com.

TeleSMS also encapsulates the logic for receiving SMS messages. Since there are many cases of incorrectly formatted messages, TeleSMS will attempt to return a nicely formatted message along with its metadata.

To send a message, you can do the following:


Telesms::Outgoing.deliver('john@example.com', '555555555', 'Verizon', 'Message body')

To receive a message, do the following:


Telesms::Incoming.receive(params[:mail])
# => { from: '555555555', to: 'john@example.com', body: 'Message body', provider: 'Verizon' }

If you have any suggestions or improvements, please let me know or create a pull request on Github.

Telefio uses the TeleSMS gem.

Passing custom attributes to mail observer

I was trying to figure out how to pass custom attributes to a Ruby on Rails mail observer (should be the same for an interceptor) and couldn’t find any documentation about it. Since the observer only receives the Mail object as a parameter in


self.delivered_email

method, we will need to pass the custom attributes through the mail object. One way to do is as follows:


mail(
  :to =>'email@sample.com',
  :custom_attr_1 => 'This is my custom string'
)

And you can receive it in the observer with the following:


def self.delivered_email(mail)
  puts mail.header[:custom_attr_1].value
end

If you have an interceptor, use the following:


def self.delivering_email(mail)
  puts mail.header[:custom_attr_1].value
end

I hope this helps someone.

Strong Parameters – Restricting Collection

Recently I was trying to find a way for strong parameters to restrict a collection to the items a user owns but I could not find anything so I decided to do something myself.

I want a Category to accept an array of member_ids but I want them to be only the members the user owns (preventing a user from assigning someone else’s members).

In the controller we expose the members and the category and only use the organization’s members:


class CategoriesController < ApplicationController
  expose(:organization)

  expose(:members) { organization.members }

  expose(:category, attributes: :category_params)

  def new
  end

  def create
    category.members &= members
    
    if category.save
      redirect_to category_path
    else
      render :new
    end
  end

private

  def category_params
    params.require(:category).permit(:name, {:member_ids => []})
  end
end

And now the category will only contain members that the organization (or user) owns. It may not be the most efficient way of doing this so if you have any suggestions please leave a comment.

LiteShops – Create a Shop in 30 Seconds

liteshops_logo
Over the weekend four of us worked on a Rails Rumble application for the annual Ruby on Rails competition. We decided to create an application that would help with selling things online easily. It is called Lite Shops.

To post a product online, simply:

  1. Take a picture(s) of your item.
  2. Email the picture(s) to sell@liteshops.com with the name as the subject (add a price with a dollar symbol, e.g. $20) and description as the body.
  3. LiteShops will do the rest: Create a shop for you and post your item on LiteShops.com.

Give it a try and tell us what you think. Also, don’t forget to vote/favorite this application at Rails Rumble.

Ruby – Phone Number to Word Combinations

I was recently looking for a phone-number-to-words Ruby solution but could not find one. I could not even find a good algorithm in any language. This told me that I needed to write one to share with others. The following code lets you convert a phone number to possible word(s) combinations using a dictionary.

Please feel free to comment with feedback or improvements.


require 'csv'

# This class provides methods for converting phone numbers 
# to possible word matches.
#
# How to use this file:
# => ruby phone_number_converter.rb -n NUMBER
# => ruby phone_number_converter.rb -f NUMBER_FILE
# => ruby phone_number_converter.rb -n NUMBER -d DICTIONARY_FILE
# => ruby phone_number_converter.rb -f NUMBER_FILE -d DICTIONARY_FILE
#
class PhoneNumberConverter
  attr_accessor :number, :number_file, :dictionary_file

  class << self
    # This method provides all the number-to-letters
    # options for conversions.
    #
    # @return [Hash]
    def keypad
      {
        '1' => %w{1},
        '2' => %w{A B C},
        '3' => %w{D E F},
        '4' => %w{G H I},
        '5' => %w{J K L},
        '6' => %w{M N O},
        '7' => %w{P Q R S},
        '8' => %w{T U V},
        '9' => %w{W X Y Z}
      }
    end

    # This method extracts all the options provided in 
    # the command and assigns them to class variables.
    def extract_options
      ARGV.each_with_index do |arg,index|
        following_arg = ARGV[index + 1].to_s
        case arg
        when '-n'
          @number = following_arg
        when '-f'
          @number_file = following_arg
        when '-d'
          @dictionary_file = following_arg
        end
      end
    end

    # This method parses a provided dictionary file and returns
    # an array of words or returns the default dictionary array.
    #
    # @return [Array]
    def dictionary
      if @dictionary_file
        CSV.read(@dictionary_file).flatten
      else
        %w{use ruby}
      end.map(&:downcase)
    end

    # This method generates a list of phone numbers from a specified
    # file or uses one number provided in the command.
    #
    # @return [Array]
    def numbers
      if @number_file
        CSV.read(@number_file).flatten
      else
        [@number]
      end.map{|n| clean_number(n) }
    end

    # This method cleans the provided phone number.
    #
    # @param [String] number
    #  The provided phone number.
    #
    # @return [String]
    def clean_number(number)
      number.gsub(/\D/, '').strip
    end

    # This method returns an array of digits from
    # the provided number.
    #
    # @param [String] number
    #  The provided phone number.
    #
    # @return [Array]
    def array_of_numbers(number)
      number.to_s.split(//)
    end

    # This method generates all the possible partitions
    # from the provided phone number.
    #
    # [1,2,3,4] => [[[1],[2,3,4]],[[1,2],[3,4]],[[1],[2,3,4]]] ...
    #
    # @param [Array] arr_nums
    #  An array of digits from the phone number.
    #
    # @return [Array]
    def partitions(arr_nums)
      (0...arr_nums.length)
      .flat_map{|i| (1...arr_nums.length).to_a.combination(i).to_a }
      .map{|cut| i = -1; arr_nums.slice_before{cut.include?(i += 1)}.to_a }
    end

    # This method generates all the possible word(s) matches
    # for the provided number and dictionary.
    #
    # @return [Array]
    def permutations
      extract_options

      numbers.collect do |phone_number|
        partitions = partitions(array_of_numbers(phone_number))
        partitions.map do |partition| 
          partition.map do |numbers|
            numbers.map do |number|
              keypad[number]
            end.inject(&:product).map do |perms| 
              numbers.one? ? perms : perms.join
            end.select do |word| 
              dictionary.include?(word.downcase) || word.length == 1
            end.collect do |word|
              dictionary.include?(word.downcase) ? word : keypad.select{|k,v| v.include?(word) }.first[0]
            end
          end.inject(&:product).collect do |word_option| 
            word_option.is_a?(Array) ? word_option.join('-') : word_option
          end
        end.reject(&:empty?).flatten.reject do |word_option| 
          word_option.length < array_of_numbers(phone_number).length || word_option =~ /[0-9]-[0-9]/
        end.uniq
      end
    end
  end
end

permutations = PhoneNumberConverter::permutations

permutations.each do |phone_number|
  puts phone_number
end

Thanks to sawa for helping out with a part of it.

Rails Routes – Dynamic Controller or Module

I was recently looking for a solution to call the correct controller depending on other data (the user that is being viewed). If the user is an admin, call one controller. If the user is a regular user, call another controller. You could redirect to the correct controllers but it’s not elegant. You could also have the controller (or module) directly in the URL but that does not look pretty.

Since there is no Rails way of doing it (and probably not the best thing to do), here is a possible solution that will probably work:


get 'users/:id', to: 'admin/users#show', constraints: lambda { |request| 
  id = request.fullpath.gsub('/users/','').to_i
  User.find_by_id(id).admin?
}

get 'users/:id', to: 'regular/users#show', constraints: lambda { |request| 
  id = request.fullpath.gsub('/users/','').to_i
  !User.find(id).admin?
}

Thanks to kardeiz for the help. Again, this is just a possible solution that can be revised and improved until someone comes up with a Rails way of doing this.

RSpec – Reusing examples

A good practice while writing RSpec tests is to reuse similar examples to make the tests DRYer and easier to read and understand.

Create a shared_examples_for block with all the reused examples and include the block with it_behaves_like where you want to use the similar examples.


describe ".trigger_action"
  let(:subject) { User.trigger_action }

  shared_examples_for "email_sent" do
    it "sends an email" do
      expect(ActionMailer::Base.deliveries).not_to be_empty
    end
  end

  context "when valid" do
    it "is true" do
      expect(subject).to be_true
    end

    it_behaves_like "email_sent"
  end

  context "when invalid" do
    it "is false" do
      expect(subject).to be_false
    end

    it_behaves_like "email_sent"
  end
end