Skip to content

Instantly share code, notes, and snippets.

@modsaid
Forked from AhmedElSharkasy/best_practices
Last active January 2, 2016 07:59
Show Gist options
  • Save modsaid/8273218 to your computer and use it in GitHub Desktop.
Save modsaid/8273218 to your computer and use it in GitHub Desktop.

Revisions

  1. modsaid revised this gist Jan 5, 2014. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions scope.rb
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,15 @@
    scope :un_deleted, where(delete_flag: false)
    scope :newly_products, -> { |start_date = 1.week.ago| where("created_at > ?", start_date) } # Yes the scope parameter can have a default
    scope :unavailable, where(delete_flag: true)

    # Default Scope
    # The default_scope is also applied while creating/building a record. It is not applied while updating a record.
    # So, in the following example, new records will have delete_flag set to false upon creation
    default_scope :available, where(delete_flag: false)

    Product.create().delete_flag # Will be false


    products = Product.for_brand("apple").price_bigger_than(1000).newly_products

    # What if we want to override the default scope?!
  2. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    # text
    Rails as it has never been before :)
  3. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions scope.rb
    Original file line number Diff line number Diff line change
    @@ -12,15 +12,15 @@
    # GOOD
    scope :price_bigger_than, -> { |min_price| where("price > ?", min_price) } # Yes you can pass parameter to scope
    scope :for_brand, -> { |brand| where(brand: brand) }
    default_scope :un_deleted, where(delete_flag: false)
    scope :new, -> { |start_date = 1.week.ago| where("created_at > ?", start_date) } # Yes the scope parameter can have a default
    scope :available, where(delete_flag: false)
    scope :un_deleted, where(delete_flag: false)
    scope :newly_products, -> { |start_date = 1.week.ago| where("created_at > ?", start_date) } # Yes the scope parameter can have a default
    scope :unavailable, where(delete_flag: true)
    default_scope :available, where(delete_flag: false)

    products = Product.for_brand("apple").price_bigger_than(1000).available.new
    products = Product.for_brand("apple").price_bigger_than(1000).newly_products

    # What if we want to override the default scope?!
    # Get all apple unavailable products
    # Get all apple unavailable products

    products = Product.for_brand("apple") # Wont work, will get only the available
    products = Product.for_brand("apple").unavailable # Wont work too, will get only the available
    @@ -37,7 +37,7 @@

    # Note: -> AND lambda are equivalent

    # You can define scope dynamically
    # You can define scope dynamically
    # Example for an api with api_keys
    Status = { normal: 0, whitelisted: 1, blacklisted: 2}
    Status.each_pair do |k,v|
  4. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    # text
    Rails as it has never been before :)
  5. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # text
  6. @AhmedElSharkasy AhmedElSharkasy created this gist Jan 2, 2014.
    1 change: 1 addition & 0 deletions README
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # text
  7. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 3 changed files with 7 additions and 6 deletions.
    6 changes: 3 additions & 3 deletions clojure.rb
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@ def do_math_operation(amount, &block)
    # The same above can be done using proc

    def do_math_operation(amount, proc)
    block.call(amount) # yield won't work!
    proc.call(amount) # yield won't work!
    end

    multiply_by_2 = Proc.new do |n|
    @@ -26,7 +26,7 @@ def do_math_operation(amount, proc)

    # The same above can be done using lambda
    def do_math_operation(amount, lambda)
    block.call(amount) # yield won't work!
    lambda.call(amount) # yield won't work!
    end

    multiply_by_2 = lambda{ |n| n * 2 }
    @@ -77,4 +77,4 @@ def multiply_by_2(n)
    result = do_math_operation(5, method(:multiply_by_2))
    => 10

    # Method objects will act like lambda , only lambda is anonymous
    # Method objects will act like lambda , only lambda is anonymous
    2 changes: 1 addition & 1 deletion presenter.rb
    Original file line number Diff line number Diff line change
    @@ -66,5 +66,5 @@ def present(object, klass = nil)
    private
    def present(object, klass = nil)
    klass ||= "#{object.class}Presenter".constantize
    klass.new(view_context, object)
    klass.new(object, view_context)
    end
    5 changes: 3 additions & 2 deletions validation.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    s# Not good
    # Not good

    class User < ActiveRecord::Base
    validates :appropriate_content

    @@ -22,4 +23,4 @@ def validate_each(record, attr, value)
    record.errors.add(:attr, "is inappropriate")
    end
    end
    end
    end
  8. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 3 changed files with 22 additions and 26 deletions.
    30 changes: 15 additions & 15 deletions meta_tags.rb → meta_tag.rb
    Original file line number Diff line number Diff line change
    @@ -1,23 +1,31 @@
    # It is common in any rails application to have some meta tags in your HTML as title, description, keywords , fb meta tags,...

    # BAD
    # in app/views/layouts/application.html.erb
    # In app/views/layouts/application.html.erb
    <meta name="description" content="<%= @description%>" />
    <meta property="og:image" content="<%= @image_url %>" />
    # in each controller
    # In each controller
    @description =
    @image_url =

    # Better but bad
    # Better
    # in app/views/layouts/application.html.erb
    <meta name="description" content="<%= yield(:description) %>" />
    <meta property="og:image" content="<%= yield(:image_url) %>" />
    # In each view
    content_for(:title, @product.title)
    content_for(:title, @product.description)

    # Best
    # Better

    <%=
    og_tags(standard_og_stuff(@product), {
    :type => 'website',
    :other_tag => 'something'
    })
    %>

    # app/helpers/application_helper.rb
    def og_tags(*tags)
    content = tags.reduce({}) do |result, set|
    result.merge! set
    @@ -31,15 +39,7 @@ def og_tags(*tags)
    # Then a helper method that pulls standard attrs (name, desc, image, ...) from a piece of content:

    def standard_og_stuff(content)
    {:title => content.name,
    :description => content.description
    { title: content.name,
    description: content.description
    }
    end
    Then combine in view:

    <%=
    og_tags(standard_og_stuff(@product), {
    :type => 'video.tv_show',
    :other_tag => 'something'
    })
    %>
    end
    14 changes: 5 additions & 9 deletions scope.rb
    Original file line number Diff line number Diff line change
    @@ -14,8 +14,8 @@
    scope :for_brand, -> { |brand| where(brand: brand) }
    default_scope :un_deleted, where(delete_flag: false)
    scope :new, -> { |start_date = 1.week.ago| where("created_at > ?", start_date) } # Yes the scope parameter can have a default
    default_scope :available, where(delete_flag: false)
    default_scope :unavailable, where(delete_flag: true)
    scope :available, where(delete_flag: false)
    scope :unavailable, where(delete_flag: true)

    products = Product.for_brand("apple").price_bigger_than(1000).available.new

    @@ -30,7 +30,7 @@
    # Now we want to get products updated today
    # One can make
    scope :most_updated, where("updated_at > ?", Date.today) # Watch Out!!!
    # A scope is defined at initialisation, so what you start up your server, console, or in my case the DelayedJob worker, Date.today gets executed once and a scope is created called due that will return all the Books that are due on the day you start the process
    # A scope is defined at initialization, so as you start up your server. Date.today gets executed once and a scope is created so a call to most_updated will return all the products that are updated on the day you start the server!

    # This must be me done using Lambda OR without scopes
    scope :most_updated, -> { where("updated_at > ?", Date.today) }
    @@ -39,13 +39,9 @@

    # You can define scope dynamically
    # Example for an api with api_keys
    Status = {:normal => 0, :whitelisted => 1, :blacklisted => 2}
    Status = { normal: 0, whitelisted: 1, blacklisted: 2}
    Status.each_pair do |k,v|
    scope "status_#{k}", where(:status => v) # ApiKey.status_whitelisted,...

    define_singleton_method "status_#{k}?" do |st| # ApiKey.status_whitelisted?(status),...
    v == st
    end
    scope "status_#{k}", where(status: v) # ApiKey.status_whitelisted,...

    define_method "status_#{k}?" do # api_key.status_whitelisted?,...
    self.status == v
    4 changes: 2 additions & 2 deletions validation.rb
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ class User < ActiveRecord::Base

    def appropriate_content
    unless # some validation check on name
    self.errors.add(:name, "is inappropriate")
    self.errors.add(:name, 'is inappropriate')
    end
    end
    end
    @@ -15,7 +15,7 @@ class User < ActiveRecord::Base
    validate :name, appropriate: true
    end

    # /lib/appropriate_validator
    # /lib/appropriate_validator.rb
    class AppropriateValidator < ActiveRecord::EachValidator
    def validate_each(record, attr, value)
    unless # some validation check on value
  9. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 8 additions and 6 deletions.
    14 changes: 8 additions & 6 deletions performant_model.rb
    Original file line number Diff line number Diff line change
    @@ -19,16 +19,18 @@


    # Counter Caching
    # we want to get the number of followers of a user
    # we want to get the number of retweets of a tweet

    # BAD
    user.followers.length # Load the followers into array and then call .length on that array
    tweet.retweets.length # Load the tweets into array and then call .length on that array

    # Better
    user.followers.size OR user.followers.count # make a count query to the db
    tweet.retweets.size OR tweet.retweets.count # make a count query to the db

    # What if the above query is made in a loop? many queries right?
    # Best using counter caching
    # Add column followers_count to the user model
    belongs_to "user", class_name "User", foreign_key: "user_id", counter_cache: :followers_count
    user.followers.size # no query , only look at the cache
    # Add column retweets_count to the tweet model
    # app/models/tweet.rb
    belongs_to :original_tweet, class_name: 'Tweet', foreign_key: 'tweet_id', counter_cache: :retweets_count
    has_many :retweets, class_name: 'Tweet', foreign_key: 'tweet_id'
    tweet.retweets.size # No query , only look at the cache as the 'tweet' object is already fetched
  10. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 10 additions and 10 deletions.
    20 changes: 10 additions & 10 deletions presenter.rb
    Original file line number Diff line number Diff line change
    @@ -1,32 +1,33 @@
    # Presenters are created by Controllers/Views and know the current state of the view and the model
    # Can be used to filter away out of tangled logic in the view
    # Can be used to filter away tangled logic in the view
    # Also can be used to skin the controller

    # An example of a presenter used to remove logic from view(this is a small example , views can get it even more bloated with logic)
    # app/views/categories/index.html
    <div class="images">
    <%if @category.image_url%>
    <%= link_to image_tag("/images/#{category.id}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%= link_to image_tag("/images/#{@category.id}.png") + "some extra text", category_path(@category), class: 'class-name', remote: true %>
    <%else%>
    <%= link_to image_tag("/images/default.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%= link_to image_tag("/images/default.png") + "some extra text", category_path(@category), class: 'class-name', remote: true %>
    <%end%>
    <div>

    # app/presenters/category_presenter.rb
    class CategoryPresenter < BasePresenter
    presents :category

    def featured_image
    image_path = category.image_url ? category.id : "default"
    link_to image_tag("/images/#{image_path}.png") + "some extra text", category_path(category), :class => 'class-name', :remote => true
    end

    def top_brands
    @top_brands ||= category.top_brands
    end

    def top_keywords
    @top_keywords ||= category.top_keywords
    end

    def featured_image
    image_path = category.image_url ? category.id : "default"
    link_to image_tag("/images/#{image_path}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true
    end
    end

    class BasePresenter
    @@ -57,8 +58,7 @@ def method_missing?(*args, &block)
    def present(object, klass = nil)
    klass ||= "#{object.class}Presenter".constantize
    presenter = object.new(object, self)
    yield presenter if block_given?
    presenter
    yield(presenter)
    end
    # To access presenters from controller
  11. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 18 additions and 1 deletion.
    19 changes: 18 additions & 1 deletion decorator.rb
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,31 @@
    # Decorators let you layer on functionality to existing operations, i.e serve a similar purpose to callbacks.
    # For cases where including callback logic in the model would give the model too many responsibilities, a Decorator is useful.

    # Imagine we have this logic on stackoverflow:
    # A user can post a question on a stackoverflow, after posting an email notification is sent to
    # all users have interest in the question topic, a post is shared on the user profile with a link to the question he posted

    # BAD
    # app/models/question.rb
    after_create :handle_after_create_logic
    private
    def handle_after_create_logic
    # queue email to be sent
    # create post
    end
    # The question model should not be responsible for all this logic

    # Good
    class SOFQuestionNotifier
    def initialize(question)
    @question = question
    end

    def save
    @question.save && post_to_wall && queue_emails
    if @question.save
    post_to_wall
    queue_emails
    end
    end

    private
  12. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 9 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions policy.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,10 @@
    # Sometimes complex read operations might deserve their own objects, Policy Object is the right solution.
    # Policy Objects are similar to Service Objects, but it is conventional to use services for write operations
    # and policies for read.
    # They are also similar to Query Objects, but Query Objects focus on executing SQL to return a result set,
    # whereas Policy Objects operate on domain models already loaded into memory.
    # app/policies/twitter_policy.rb

    class TwitterPolicy < Struct.new(:auth)

    def first_name
    @@ -16,7 +22,7 @@ def last_name
    # app/policies/facebook_policy.rb
    class FacebookPolicy < Struct.new(:auth)
    def first_name
    auth['info']['name'].split(' ').first
    auth['info']['first_name']
    end

    def last_name
    @@ -32,4 +38,5 @@ def self.from_oauth(auth)
    create_user_from_policy(policy)
    end

    # check https://github.com/elabs/pundit for maximum usage of policies
    # Check https://github.com/elabs/pundit for maximum usage of policies and OO design
    # Pundit: Minimal authorization through OO design and pure Ruby classes
  13. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 25 additions and 13 deletions.
    38 changes: 25 additions & 13 deletions service.rb
    Original file line number Diff line number Diff line change
    @@ -1,19 +1,31 @@
    v# User service object when:
    # 1- The action is complex
    # 2- The action reaches across multiple models (e.g. an e-commerce purchase using Order, CreditCard and Customer objects)
    # Use service object when:
    # 1- The action is complex.
    # 2- The action reaches across multiple models.
    # 3- The action interacts with an external service (e.g. posting to social networks)
    # 4- The action is not a core concern of the underlying model (e.g. sweeping up outdated data after a certain time period).
    # 5- There are multiple ways of performing the action (e.g. authenticating with an access token or password).

    class UserCSVDump < Struct.new(:users)
    def csv
    CSV.generate do |csv|
    csv << %w[id username email]
    users.each do |user|
    csv << [user.id, user.username, user.email]
    end
    end
    class Authentication
    def initialize(params, omniauth = nil)
    @params = params
    @omniauth = omniauth
    end
    end

    # Another example would be a class to manage authentication
    def user
    @user = @omniauth ? user_from_omniauth : user_with_password
    end

    def authenticated?
    user.present?
    end

    private

    def user_from_omniauth
    # Authenticate with omniauth
    end

    def user_with_password
    # Authenticate with password
    end
    end
  14. @AhmedElSharkasy AhmedElSharkasy renamed this gist Jan 2, 2014. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions law_of_demeter_model.rb → demeter_model.rb
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,12 @@
    # Law Of Demeter: Every unit should have limited knowledge about other units, simply don't talk to strangers!
    # 2 dots are fair enough!

    # BAD
    class Invoice < ActiveRecord::Base
    belongs_to :user
    end

    # In model/view
    invoice.user.name

    # Good
  15. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 9 additions and 6 deletions.
    15 changes: 9 additions & 6 deletions concern_module.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    # app/models/concerns/users/csv_conversion.rb

    class User
    module CsvConversion
    extend ActiveSupport::Concern
    @@ -19,18 +20,19 @@ def to_csv(options = {})
    # app/models/user.rb
    include CsvConversion


    # Use modules for more complex and widely used logic

    # Using modules/plugins
    # in lib/plugins/sponsorable.rb
    module Sponsorable
    extend ActiveSupport::Concern
    module ClassMethods
    def acts_as_sponsorable(configuration = {})
    # do your logic and define the needed associations ex: product has many sponsors,
    # a sponsor may sponsor many products , yes a polymorphic association
    # Do your logic and define the needed associations ex: product has many sponsors,
    # A sponsor may sponsor many products , yes a polymorphic association
    end
    # if you forgot this line all instances of ActiveRecord::Base will have these methods !

    # If you forgot this line all instances of ActiveRecord::Base will have these methods !
    include InstanceMethods
    end

    @@ -39,12 +41,13 @@ def sponsors_count
    end
    end
    end
    ActiveRecord::Base.send :include, Sponsorable

    ActiveRecord::Base.send(:include, Sponsorable)

    # config/initializers/extension.rb
    require 'sponsorable'

    # app/models/product.rb
    acts_as_sponsorable({max_count: 5, sponsors_type: "exclusive"})
    acts_as_sponsorable({ max_count: 5, sponsors_type: 'exclusive' })

    # Use modules/plugins for shared complex logic and use concerns for model related simple logic
  16. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 2, 2014. 1 changed file with 19 additions and 17 deletions.
    36 changes: 19 additions & 17 deletions clojure.rb
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,17 @@
    # If the last parameter in a method definition is prefixed with an ampersand, any associated block is converted to a Proc object, # and that object is assigned to the parameter. It must be the last argument in the parameter list
    # If the last parameter in a method definition is prefixed with an ampersand, any associated block
    # is converted to a Proc object and that object is assigned to the parameter.
    # It must be the last argument in the parameter list
    def do_math_operation(amount, &block)
    block.call(amount) # OR yield(amount)
    end

    result = do_math_operation(5) {|amount| amount * 2}
    => 10

    # a proc is a reusable object oriented block , a block is a proc that can't be saved
    # A proc is a reusable object oriented block , a block is actually a proc that can't be saved
    block.class => Proc

    # the same above can be done using proc
    # The same above can be done using proc

    def do_math_operation(amount, proc)
    block.call(amount) # yield won't work!
    @@ -19,41 +21,41 @@ def do_math_operation(amount, proc)
    n * 2
    end

    result = do_math_operation(5,multiply_by_2)
    result = do_math_operation(5, multiply_by_2)
    => 10

    # the same above can be done using lambda
    # The same above can be done using lambda
    def do_math_operation(amount, lambda)
    block.call(amount) # yield won't work!
    end

    multiply_by_2 = lambda{ |n| n * 2 }
    result = do_math_operation(5,multiply_by_2)
    result = do_math_operation(5, multiply_by_2)
    => 10

    # Differences between Lambda and Procs
    1- Lambda checks for number of parameters , throw an expection if less or more parameters were passed
    ex:
    # 1- Lambda checks for number of parameters , throw an exception if less or more parameters were passed
    # ex:
    lambda = lambda{ |str1, str2| "#{str1}, #{str2}"}
    lambda.call("str1","str2")
    lambda.call('str1', 'str2')
    => str1, str2
    lambda.call("str2","str2","str3")
    lambda.call('str2','str2','str3')
    ArgumentError: wrong number of arguments (3 for 2)

    proc = Proc.new{ |str1, str2| "#{str1}, #{str2}" }
    proc.call("str1")
    => str1,

    2- Lambda in a method will return the value to the method and the method continues normally, while Proc stops method execution
    # 2- Lambda in a method will return the value to the method and the method continues normally, while Proc stops method execution

    def proc_return
    Proc.new{|n| puts n}
    puts "see me if you can"
    Proc.new{ |n| puts n }
    puts 'see me if you can'
    end

    def lambda_return
    lambda{|n| puts n}
    puts "hi, i am here"
    lambda{ |n| puts n }
    puts 'hi, i am here'
    end

    proc_return
    @@ -63,7 +65,7 @@ def lambda_return

    # Note proc can't have a return in it , while lambda can

    # Using method objects
    # The same can be done using method objects
    def do_math_operation(amount, method)
    block.call(amount) # yield won't work!
    end
    @@ -72,7 +74,7 @@ def multiply_by_2(n)
    n * 2
    end

    result = do_math_operation(5,method(:multiply_by_2))
    result = do_math_operation(5, method(:multiply_by_2))
    => 10

    # Method objects will act like lambda , only lambda is anonymous
  17. @AhmedElSharkasy AhmedElSharkasy renamed this gist Jan 1, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  18. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion service.rb
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@ class UserCSVDump < Struct.new(:users)
    def csv
    CSV.generate do |csv|
    csv << %w[id username email]
    @users.each do |user|
    users.each do |user|
    csv << [user.id, user.username, user.email]
    end
    end
    2 changes: 1 addition & 1 deletion validation.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # Not good
    s# Not good
    class User < ActiveRecord::Base
    validates :appropriate_content

  19. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 4 changed files with 79 additions and 7 deletions.
    6 changes: 0 additions & 6 deletions method_calling.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +0,0 @@
    # If the last parameter in a method definition is prefixed with an ampersand, any associated block is converted to a Proc object, # and that object is assigned to the parameter. It must be the last argument in the parameter list
    def do_math_operation(amount, &block)
    block.call(amount)
    end

    result = do_math_operation(5) {|amount| amount * 2} => 10
    78 changes: 78 additions & 0 deletions procs_lambdas_blocks.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    # If the last parameter in a method definition is prefixed with an ampersand, any associated block is converted to a Proc object, # and that object is assigned to the parameter. It must be the last argument in the parameter list
    def do_math_operation(amount, &block)
    block.call(amount) # OR yield(amount)
    end

    result = do_math_operation(5) {|amount| amount * 2}
    => 10

    # a proc is a reusable object oriented block , a block is a proc that can't be saved
    block.class => Proc

    # the same above can be done using proc

    def do_math_operation(amount, proc)
    block.call(amount) # yield won't work!
    end

    multiply_by_2 = Proc.new do |n|
    n * 2
    end

    result = do_math_operation(5,multiply_by_2)
    => 10

    # the same above can be done using lambda
    def do_math_operation(amount, lambda)
    block.call(amount) # yield won't work!
    end

    multiply_by_2 = lambda{ |n| n * 2 }
    result = do_math_operation(5,multiply_by_2)
    => 10

    # Differences between Lambda and Procs
    1- Lambda checks for number of parameters , throw an expection if less or more parameters were passed
    ex:
    lambda = lambda{ |str1, str2| "#{str1}, #{str2}"}
    lambda.call("str1","str2")
    => str1, str2
    lambda.call("str2","str2","str3")
    ArgumentError: wrong number of arguments (3 for 2)

    proc = Proc.new{ |str1, str2| "#{str1}, #{str2}" }
    proc.call("str1")
    => str1,

    2- Lambda in a method will return the value to the method and the method continues normally, while Proc stops method execution

    def proc_return
    Proc.new{|n| puts n}
    puts "see me if you can"
    end

    def lambda_return
    lambda{|n| puts n}
    puts "hi, i am here"
    end

    proc_return
    => Proc
    lambda_return
    => hi, i am here

    # Note proc can't have a return in it , while lambda can

    # Using method objects
    def do_math_operation(amount, method)
    block.call(amount) # yield won't work!
    end

    def multiply_by_2(n)
    n * 2
    end

    result = do_math_operation(5,method(:multiply_by_2))
    => 10

    # Method objects will act like lambda , only lambda is anonymous
    2 changes: 1 addition & 1 deletion service.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # User service object when:
    v# User service object when:
    # 1- The action is complex
    # 2- The action reaches across multiple models (e.g. an e-commerce purchase using Order, CreditCard and Customer objects)
    # 3- The action interacts with an external service (e.g. posting to social networks)
    File renamed without changes.
  20. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions method_calling.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    # If the last parameter in a method definition is prefixed with an ampersand, any associated block is converted to a Proc object, # and that object is assigned to the parameter. It must be the last argument in the parameter list
    def do_math_operation(amount, &block)
    block.call(amount)
    end

    result = do_math_operation(5) {|amount| amount * 2} => 10
  21. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 1 changed file with 16 additions and 1 deletion.
    17 changes: 16 additions & 1 deletion scope.rb
    Original file line number Diff line number Diff line change
    @@ -35,4 +35,19 @@
    # This must be me done using Lambda OR without scopes
    scope :most_updated, -> { where("updated_at > ?", Date.today) }

    # Last Note: -> AND lambda are equivalent
    # Note: -> AND lambda are equivalent

    # You can define scope dynamically
    # Example for an api with api_keys
    Status = {:normal => 0, :whitelisted => 1, :blacklisted => 2}
    Status.each_pair do |k,v|
    scope "status_#{k}", where(:status => v) # ApiKey.status_whitelisted,...

    define_singleton_method "status_#{k}?" do |st| # ApiKey.status_whitelisted?(status),...
    v == st
    end

    define_method "status_#{k}?" do # api_key.status_whitelisted?,...
    self.status == v
    end
    end
  22. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 1 changed file with 41 additions and 3 deletions.
    44 changes: 41 additions & 3 deletions meta_tags.rb
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,45 @@
    # It is common in any rails application to have some meta tags in your HTML as title, description, ...
    # It is common in any rails application to have some meta tags in your HTML as title, description, keywords , fb meta tags,...

    # BAD
    # in app/views/layouts/application.html.erb
    <meta name="description" content="<%= @description%>" />
    <meta property="og:image" content="<%= @image_url %>" />
    # in each controller
    @description =
    @image_url =

    # Better
    # Better but bad
    # in app/views/layouts/application.html.erb
    <meta name="description" content="<%= yield(:description) %>" />
    <meta property="og:image" content="<%= yield(:image_url) %>" />
    # In each view
    content_for(:title, @product.title)
    content_for(:title, @product.description)

    # Best
    # Best

    def og_tags(*tags)
    content = tags.reduce({}) do |result, set|
    result.merge! set
    end

    raw(content.map do |key, value|
    tag :meta, content: value, property: "og:#{key}"
    end.join("\n"))
    end

    # Then a helper method that pulls standard attrs (name, desc, image, ...) from a piece of content:

    def standard_og_stuff(content)
    {:title => content.name,
    :description => content.description
    }
    end
    Then combine in view:

    <%=
    og_tags(standard_og_stuff(@product), {
    :type => 'video.tv_show',
    :other_tag => 'something'
    })
    %>
  23. @AhmedElSharkasy AhmedElSharkasy revised this gist Jan 1, 2014. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions meta_tags.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    # It is common in any rails application to have some meta tags in your HTML as title, description, ...

    # BAD

    # Better

    # Best
  24. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 10 additions and 5 deletions.
    15 changes: 10 additions & 5 deletions presenter.rb
    Original file line number Diff line number Diff line change
    @@ -4,11 +4,13 @@

    # An example of a presenter used to remove logic from view(this is a small example , views can get it even more bloated with logic)
    # app/views/categories/index.html
    <%if @category.image_url%>
    <%= link_to image_tag("/images/#{category.id}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%else%>
    <%= link_to image_tag("/images/default.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%end%>
    <div class="images">
    <%if @category.image_url%>
    <%= link_to image_tag("/images/#{category.id}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%else%>
    <%= link_to image_tag("/images/default.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%end%>
    <div>

    # app/presenters/category_presenter.rb
    class CategoryPresenter < BasePresenter
    @@ -46,6 +48,9 @@ def method_missing?(*args, &block)

    # app/views/category/index.html
    <% present @category do |category_presenter|%>
    <div class="images">
    <%= category_presenter.featured_image%>
    <div>
    <% end %>
    # app/helpers/application_helper.rb
  25. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion presenter.rb
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ def top_keywords

    def featured_image
    image_path = category.image_url ? category.id : "default"
    link_to image_tag("/images/#{image_path}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    link_to image_tag("/images/#{image_path}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true
    end
    end

  26. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 12 additions and 9 deletions.
    21 changes: 12 additions & 9 deletions presenter.rb
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,14 @@
    # Imagine you have a page with lists products in an ecommerce site with side bar containing top discounts in this category,
    # Top brands, top search keywords,...
    # probably your action will look like this:
    # Presenters are created by Controllers/Views and know the current state of the view and the model
    # Can be used to filter away out of tangled logic in the view
    # Also can be used to skin the controller


    # GOOD
    def show
    present(:category).to_json
    end
    # An example of a presenter used to remove logic from view(this is a small example , views can get it even more bloated with logic)
    # app/views/categories/index.html
    <%if @category.image_url%>
    <%= link_to image_tag("/images/#{category.id}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%else%>
    <%= link_to image_tag("/images/default.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    <%end%>

    # app/presenters/category_presenter.rb
    class CategoryPresenter < BasePresenter
    @@ -20,7 +22,8 @@ def top_keywords
    end

    def featured_image
    link_to_if category.image_url, image_tag("images/#{category.id}", class: "cat-featured-image")
    image_path = category.image_url ? category.id : "default"
    link_to image_tag("/images/#{image_path}.png") + "some extra text", category_path(@category), :class => 'class-name', :remote => true %>
    end
    end

  27. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 1 addition and 11 deletions.
    12 changes: 1 addition & 11 deletions presenter.rb
    Original file line number Diff line number Diff line change
    @@ -2,19 +2,9 @@
    # Top brands, top search keywords,...
    # probably your action will look like this:

    # BAD
    def index
    @products =
    @top_brands =
    @discounts =
    @top_keywords =
    ........
    end

    # Use Presenters

    # GOOD
    def index
    def show
    present(:category).to_json
    end

  28. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 72 additions and 1 deletion.
    73 changes: 72 additions & 1 deletion presenter.rb
    Original file line number Diff line number Diff line change
    @@ -1 +1,72 @@
    # Content
    # Imagine you have a page with lists products in an ecommerce site with side bar containing top discounts in this category,
    # Top brands, top search keywords,...
    # probably your action will look like this:

    # BAD
    def index
    @products =
    @top_brands =
    @discounts =
    @top_keywords =
    ........
    end

    # Use Presenters

    # GOOD
    def index
    present(:category).to_json
    end

    # app/presenters/category_presenter.rb
    class CategoryPresenter < BasePresenter
    presents :category
    def top_brands
    @top_brands ||= category.top_brands
    end

    def top_keywords
    @top_keywords ||= category.top_keywords
    end

    def featured_image
    link_to_if category.image_url, image_tag("images/#{category.id}", class: "cat-featured-image")
    end
    end

    class BasePresenter
    def initialize(object, template)
    @object = object
    @template = template
    end

    def self.presents(name)
    define_method(name) do
    @object
    end
    end

    def method_missing?(*args, &block)
    @template.send(*args, &block)
    end
    end

    # app/views/category/index.html
    <% present @category do |category_presenter|%>
    <% end %>
    # app/helpers/application_helper.rb
    def present(object, klass = nil)
    klass ||= "#{object.class}Presenter".constantize
    presenter = object.new(object, self)
    yield presenter if block_given?
    presenter
    end
    # To access presenters from controller
    # app/controllers/application_controller.rb
    private
    def present(object, klass = nil)
    klass ||= "#{object.class}Presenter".constantize
    klass.new(view_context, object)
    end
  29. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 31, 2013. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions presenter.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Content
  30. @AhmedElSharkasy AhmedElSharkasy revised this gist Dec 30, 2013. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions concern_module.rb
    Original file line number Diff line number Diff line change
    @@ -19,6 +19,8 @@ def to_csv(options = {})
    # app/models/user.rb
    include CsvConversion



    # Using modules/plugins
    # in lib/plugins/sponsorable.rb
    module Sponsorable
    @@ -28,6 +30,7 @@ def acts_as_sponsorable(configuration = {})
    # do your logic and define the needed associations ex: product has many sponsors,
    # a sponsor may sponsor many products , yes a polymorphic association
    end
    # if you forgot this line all instances of ActiveRecord::Base will have these methods !
    include InstanceMethods
    end