Thursday, 24 July 2014

activesupportconcern

FROM: http://engineering.appfolio.com/2013/06/17/ruby-mixins-activesupportconcern/



Ruby Mixins & ActiveSupport::Concern

A few people have asked: what is the dealio with ActiveSupport::Concern? My answer: it encapsulates a few common patterns for building modules intended for mixins. Before understanding why ActiveSupport::Concern is useful, we first need to understand Ruby mixins.
Here we go…!

First, the Ruby object model:

As you can see mixins are “virtual classes” that have been injected in a class’s or module’s ancestor chain. That is, this:
module MyMod
end

class Base
end

class Child < Base
  include MyMod
end

# irb> Child.ancestors
#  => [Child, MyMod, Base, Object, Kernel, BasicObject]
results in the same ancestor chain as this:
class Base
end

class MyMod < Base
end

class Child < MyMod
end

# irb> Child.ancestors
#  => [Child, MyMod, Base, Object, Kernel, BasicObject]
Great? Great.
Modules can also be used to extend objects, for example:
my_obj = Object.new
my_obj.extend MyMod

# irb> my_obj.singleton_class.ancestors
#  => [MyMod, Object, Kernel, BasicObject]
In Ruby, every object has a singleton class. Object#extend does what Module#include does but on an object’s singleton class. That is, the following is equivalent to the above:
my_obj = Object.new
my_obj.singleton_class.class_eval do
  include MyMod
end

# irb> my_obj.singleton_class.ancestors
#  => [MyMod, Object, Kernel, BasicObject]
This is how “static” or “class” methods work in Ruby. Actually, there’s no such thing as static/class methods in Ruby. Rather, there are methods on a class’s singleton class. For example, w.r.t. the ancestors chain, the following are equivalent:
class MyClass
  extend MyMod
end

# irb> MyClass.singleton_class.ancestors
#  => [MyMod, Class, Module, Object, Kernel, BasicObject]

class MyClass
  class << self
    include MyMod
  end
end

# irb> MyClass.singleton_class.ancestors
#  => [MyMod, Class, Module, Object, Kernel, BasicObject]
Classes are just objects “acting” as “classes”.

Back to mixins…

For example:
module MyMod
  def self.included(target)
    puts "included into #{target}"
  end

  def self.extended(target)
    puts "extended into #{target}"
  end
end

class MyClass
  include MyMod
end
# irb>
# included into MyClass

class MyClass2
  extend MyMod
end
# irb>
# extended into MyClass2

# irb> MyClass.ancestors
# => [MyClass, MyMod, Object, Kernel, BasicObject]
# irb> MyClass.singleton_class.ancestors
# => [Class, Module, Object, Kernel, BasicObject]

# irb> MyClass2.ancestors
# => [MyClass2, Object, Kernel, BasicObject]
# irb> MyClass2.singleton_class.ancestors
# => [MyMod, Class, Module, Object, Kernel, BasicObject]
Great? Great.

Back to ActiveSupport::Concern…

Over time it became a common pattern in the Ruby worldz to create modules intended for use as mixins like this:
module MyMod
  def self.included(target)
    target.send(:include, InstanceMethods)
    target.extend ClassMethods
    target.class_eval do
      a_class_method
    end
  end

  module InstanceMethods
    def an_instance_method
    end
  end

  module ClassMethods
    def a_class_method
      puts "a_class_method called"
    end
  end
end

class MyClass
  include MyMod
# irb> end
# a_class_method called
end

# irb> MyClass.ancestors
#  => [MyClass, MyMod::InstanceMethods, MyMod, Object, Kernel, BasicObject]
# irb> MyClass.singleton_class.ancestors
#  => [MyMod::ClassMethods, Class, Module, Object, Kernel, BasicObject]
As you can see, this single module is adding instance methods, “class” methods, and acting directly on the target class (calling a_class_method() in this case).
ActiveSupport::Concern encapsulates this pattern. Here’s the same module rewritten to use ActiveSupport::Concern:
module MyMod
  extend ActiveSupport::Concern

  included do
    a_class_method
  end

  def an_instance_method
  end

  module ClassMethods
    def a_class_method
      puts "a_class_method called"
    end
  end
end
You’ll notice the nested InstanceMethods is removed and an_instance_method() is defined directly on the module. This is because this is standard Ruby; given the ancestors of MyClass ([MyClass, MyMod::InstanceMethods, MyMod, Object, Kernel]) there’s no need for a MyMod::InstanceMethods since methods on MyMod are already in the chain.
So far ActiveSupport::Concern has taken away some of the boilerplate code used in the pattern: no need to define an included hook, no need to extend the target class with ClassMethods, no need to class_eval on the target class.
The last thing that ActiveSupport::Concern does is what I call lazy evaluation. What’s that?

Back to Ruby mixins…

Consider:
module MyModA
end

module MyModB
  include MyModA
end

class MyClass
  include MyModB
end

# irb> MyClass.ancestors
#  => [MyClass, MyModB, MyModA, Object, Kernel, BasicObject]
Let’s say MyModA wanted to do something special when included into the target class, say:
module MyModA
  def self.included(target)
    target.class_eval do
      has_many :squirrels
    end
  end
end
When MyModA is included in MyModB, the code in the included() hook will run, and if has_many() is not defined on MyModB things will break:
irb :050 > module MyModB
irb :051?>   include MyModA
irb :052?> end
NoMethodError: undefined method `has_many' for MyModB:Module
 from (irb):46:in `included'
 from (irb):45:in `class_eval'
 from (irb):45:in `included'
 from (irb):51:in `include'
ActiveSupport::Concern skirts around this issue by delaying all the included hooks from running until a module is included into a non-ActiveSupport::Concern. Redefining the above using ActiveSupport::Concern:
module MyModA
  extend ActiveSupport::Concern

  included do
    has_many :squirrels
  end
end

module MyModB
  extend ActiveSupport::Concern
  include MyModA
end

class MyClass
  def self.has_many(*args)
    puts "has_many(#{args.inspect}) called"
  end

  include MyModB
# irb>
# has_many([:squirrels]) called
end

# irb> MyClass.ancestors
#  => [MyClass, MyModB, MyModA, Object, Kernel, BasicObject]
# irb> MyClass.singleton_class.ancestors
#  => [Class, Module, Object, Kernel, BasicObject]
Great? Great.
But why is ActiveSupport::Concern called “Concern”? The name Concern comes from AOP (http://en.wikipedia.org/wiki/Aspect-oriented_programming). Concerns in AOP encapsulate a “cohesive area of functionality”. Mixins act as Concerns when they provide cohesive chunks of functionality to the target class. Turns out using mixins in this fashion is a very common practice.
ActiveSupport::Concern provides the mechanics to encapsulate a cohesive chunk of functionality into a mixin that can extend the behavior of the target class by annotating the class’ ancestor chain, annotating the class’ singleton class’ ancestor chain, and directly manipulating the target class through the included() hook.
So….
Is every mixin a Concern? No. Is every ActiveSupport::Concern a Concern? No.
While I’ve used ActiveSupport::Concern to build actual Concerns, I’ve also used it to avoid writing out the boilerplate code mentioned above. If I just need to share some instance methods and nothing else, then I’ll use a bare module.
Modules, mixins and ActiveSupport::Concern are just tools in your toolbox to accomplish the task at hand. It’s up to you to know how the tools work and when to use them.
I hope that helps somebody.

config

require 'active_support/configurable'
module GemModule
include ActiveSupport::Configurable
class << self
def setup
yield config
end
end
end
And then you can write
GemModule.setup do |config|
config.option1 = "value1"
config.option2 = 2
end
And
GemModule.config.option1
view raw gistfile1.rb hosted with ❤ by GitHub

Thursday, 17 July 2014

gzip_net_http.rb

# from http://pushandpop.blogspot.com.au/2011/05/handling-gzip-responses-in-ruby-nethttp.html
# i wanted syntax highlighting
require 'net/http'
debug = Proc.new{|msg| STDERR.puts "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] #{msg}" }
page = nil
http = Net::HTTP.new( "www.google.com", 80 )
req = Net::HTTP::Get.new( "/search?num=20&hl=en&noj=1&q=test&btnG=Search", { "Accept-Encoding" => "gzip", "User-Agent" => "gzip" } )
debug.call( "Performing HTTP GET request for (#{req.path})." )
res = http.request( req )
debug.call( "Received HTTP Response Code (#{res.code})" )
case res
when Net::HTTPSuccess then
begin
if res.header[ 'Content-Encoding' ].eql?( 'gzip' ) then
debug.call( "Performing gzip decompression for response body." )
sio = StringIO.new( res.body )
gz = Zlib::GzipReader.new( sio )
page = gz.read()
debug.call( "Finished decompressing gzipped response body." )
else
debug.call( "Page is not compressed. Using text response body. " )
page = res.body
end
rescue Exception
debug.call( "Error occurred (#{$!.message})" )
# handle errors
raise $!.message
end
end
puts page

ancestors and descendants

#ancestors
Class.ancestors
=> [Class, Module, ActiveSupport::Dependencies::ModuleConstMissing, Object, Netfira::WebConnect, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
#descendants
ObjectSpace.each_object(Class) do |klass|
p klass if klass < StandardError
end
String.subclasses
#=> [SQLite3::Blob, ActiveSupport::JSON::Encoding::JSONGemEncoder::EscapedString, Arel::Nodes::SqlLiteral]
view raw gistfile1.rb hosted with ❤ by GitHub

parents

def parents(obj)
( (obj.superclass ? parents(obj.superclass) : []) << obj). reverse
end
parents(IO).inspect
# => "[IO, BasicObject, Object]"
parents(Tempfile).inspect
#=> "[Tempfile, Delegator, BasicObject, #<Class:0x007f817cff1d60>]"
view raw gistfile1.rb hosted with ❤ by GitHub

Thursday, 3 July 2014

Amazing ActiveResource

#Create a new Blog
#rails g Blog
#rails g scaffold Post title body:text
#Using Basic auth
class ApplicationController < ActionController::Base
http_basic_authenticate_with name: "test", password: "test"
end
#rails s
#from console
#2.1-head :039 > require 'active_resource'
#true
class Post < ActiveResource::Base
self.site = "http://test:test@localhost:3000"
end
#2.1-head :036 >post = Post.new
#2.1-head :036 >post.title = "works?"
#2.1-head :036 >post.save
#=> true
view raw gistfile1.rb hosted with ❤ by GitHub

Wednesday, 2 July 2014

Dash App

Dash is an API Documentation Browser and Code Snippet Manager. Dash stores snippets of code and instantly searches offline documentation sets for 150+ APIs 


http://kapeli.com/dash


Amazing RI

#Generate all docs
-> rvm docs generate all
#use interactive
ri -i
Start typing and hit tab for autocomplete
ActiveSupport:: tab
ActiveSupport::Autoload ActiveSupport::KeyGenerator ActiveSupport::TestCase
ActiveSupport::BacktraceCleaner ActiveSupport::LegacyKeyGenerator ActiveSupport::Testing
ActiveSupport::Benchmarkable ActiveSupport::LogSubscriber ActiveSupport::TimeWithZone
ActiveSupport::Cache ActiveSupport::Logger ActiveSupport::TimeZone
ActiveSupport::CachingKeyGenerator ActiveSupport::MessageEncryptor ActiveSupport::VERSION
ActiveSupport::Callbacks ActiveSupport::MessageVerifier ActiveSupport::XMLConverter
ActiveSupport::Concern ActiveSupport::Multibyte ActiveSupport::XmlMini
ActiveSupport::Concurrency ActiveSupport::Notifications ActiveSupport::XmlMini_JDOM
ActiveSupport::Configurable ActiveSupport::NumberHelper ActiveSupport::XmlMini_LibXML
ActiveSupport::Dependencies ActiveSupport::OptionMerger ActiveSupport::XmlMini_LibXMLSAX
ActiveSupport::Deprecation ActiveSupport::OrderedHash ActiveSupport::XmlMini_Nokogiri
ActiveSupport::DeprecationException ActiveSupport::OrderedOptions ActiveSupport::XmlMini_NokogiriSAX
ActiveSupport::DescendantsTracker ActiveSupport::PerThreadRegistry ActiveSupport::XmlMini_REXML
ActiveSupport::Duration ActiveSupport::ProxyObject ActiveSupport::eager_load!
ActiveSupport::FileUpdateChecker ActiveSupport::Railtie ActiveSupport::execute_hook
ActiveSupport::FileWatcher ActiveSupport::Rescuable ActiveSupport::gem_version
ActiveSupport::Gzip ActiveSupport::SafeBuffer ActiveSupport::on_load
ActiveSupport::HashWithIndifferentAccess ActiveSupport::StringInquirer ActiveSupport::run_load_hooks
ActiveSupport::Inflector ActiveSupport::Subscriber ActiveSupport::version
ActiveSupport::InheritableOptions ActiveSupport::SubscriberQueueRegistry
ActiveSupport::JSON ActiveSupport::TaggedLogging
#Now you have access to all resources :)
view raw gistfile1.sh hosted with ❤ by GitHub

RSpec::Matchers

RSpec::Matchers#methods:
a_block_changing a_falsy_value an_instance_of be_between covering including start_with
a_block_outputting a_hash_including an_object_eq_to be_falsey end_with match starting_with
a_block_raising a_kind_of an_object_eql_to be_falsy ending_with match_array throw_symbol
a_block_throwing a_nil_value an_object_equal_to be_instance_of eq match_regex throwing
a_block_yielding_control a_range_covering an_object_existing be_kind_of eq_to matching within
a_block_yielding_successive_args a_string_ending_with an_object_matching be_nil eql output yield_control
a_block_yielding_with_args a_string_including an_object_responding_to be_truthy eql_to raise_error yield_successive_args
a_block_yielding_with_no_args a_string_matching an_object_satisfying be_within equal raise_exception yield_with_args
a_collection_containing_exactly a_string_starting_with be change equal_to raising yield_with_no_args
a_collection_ending_with a_truthy_value be_a changing exist respond_to yielding_control
a_collection_including a_value be_a_kind_of contain_exactly existing responding_to yielding_successive_args
a_collection_starting_with a_value_between be_an containing_exactly expect satisfy yielding_with_args
a_falsey_value a_value_within be_an_instance_of cover include satisfying yielding_with_no_args
view raw gistfile1.rb hosted with ❤ by GitHub

whereis?

Yannis:web-connect-client yannis$ whatis ruby
erb(1) - Ruby Templating
irb(1) - Interactive Ruby Shell
rake(1) - Ruby Make
ri(1) - Ruby API reference front end
ruby(1) - Interpreted object-oriented scripting language
Yannis:web-connect-client yannis$ whereis ruby
/usr/bin/ruby
Yannis:web-connect-client yannis$
Yannis:web-connect-client yannis$ whatis bash
bash(1) - GNU Bourne-Again SHell
bashbug(1) - report a bug in bash
Yannis:web-connect-client yannis$ whereis bash
/bin/bash
Yannis:web-connect-client yannis$
view raw gistfile1.bat hosted with ❤ by GitHub

Tuesday, 1 July 2014

Deep hash clean (with refinements)

module MyRefinements
refine Hash do
def clean!
self.delete_if do |key, val|
if block_given?
yield(key,val)
else
# Prepeare the tests
test1 = val.nil?
test2 = val === 0
test3 = val === false
test4 = val.empty? if val.respond_to?('empty?')
test5 = val.strip.empty? if val.is_a?(String) && val.respond_to?('empty?')
# Were any of the tests true
test1 || test2 || test3 || test4 || test5
end
end
self.each do |key, val|
if self[key].is_a?(Hash) && self[key].respond_to?('clean!')
if block_given?
self[key] = self[key].clean!(&Proc.new)
else
self[key] = self[key].clean!
end
end
end
return self
end
end
end
Class MyClass
using MyRefinements
#start using {}.clean!
end
view raw gistfile1.rb hosted with ❤ by GitHub

Translatable String

class TranslatedString
def initialize(owner, key)
@owner, @key = owner, key
end
def [](lang)
@owner.get_translated_value(@key, lang)
end
def to_h
@owner.translated_values[@key]
end
def []=(lang, value)
@owner.set_translated_value(@key, value, lang)
end
end
class Product
module Translatable
def translated_values
@translated_values ||= {}
end
def value_of_translated(key)
translated_values[key] ||= {}
end
def get_translated_value(key, lang = default_locale)
value_of_translated(key)[lang]
end
def set_translated_value(key, value, lang = default_locale)
if value.is_a? Hash
value.each {|lang, value| value_of_translated(key)[lang] = value}
else
value_of_translated(key)[lang] = value
end
end
def get_translated_string(key)
instance_variable_set("@#{key}", TranslatedString.new(self, key)) unless instance_variable_get("@#{key}")
instance_variable_get("@#{key}")
end
def set_translated_string(key, value)
get_translated_string(key)[default_locale] = value
end
end
end
class Product
include Translatable
def default_locale
:en_AU
end
def description
get_translated_string(:description)
end
def description=(value)
set_translated_string(:description, value)
end
end
product = Product.new
product.description[:en] = 'foo'
product.description = "foo"
product.description = {en_AU: "description", el_GR: "περιγραφή"}
product.description.to_h
view raw gistfile1.rb hosted with ❤ by GitHub