Ruby ARTestHelper

# A mixin for Test::Unit classes to ease AR testing
#
# Use in your test class by adding
#
#   include ARTestHelper
#
# Written by Nicholas Seckar aka Ulysses
module ARTestHelper
  module InstanceMethods
    # Access the temporary instance to test.
    # In my setup method, I store a new, valid instance in an instance variable such as @p.
    #
    # The ivar is the first character of the class (@p for Page, @u for User), and usually has an
    # attr_reader for it.
    #
    # This method should return that instance so that the validation checker can access it.
    def instance
      instance_variable_get("@#{self.class.name[0, 1].downcase}")
    end
  end
  
  module ClassMethods
    # Test that the named associations can be loaded
    # Catches errors such as missing tables, classes, etc.
    # Only use one directive per test-case
    #
    # Example: check_associations %w(friends downloads links)
    def check_associations(*names)
      define_method "test_associations" do
        assert(instance.save, 'Must be able to save instance!') if instance.new_record?
        names.flatten.each do |assoc|
          v = instance.send assoc
          v.each { } if v.respond_to?(:each) # Load collections by using each
        end
      end
    end

    # Check the validations for the named field
    # 
    # Example:
    #
    # check_validations_for :email do
    #   invalid nil, "can't be blank"
    #   invalid 'boo', /not .* valid .* email/
    #   valid 'fakeuser@host.com'
    # end
    def check_validations_for(field, &checks)
      define_method("test_validations_for_#{field}") do
        ValidationChecker.new(field, checks, self, instance).run
      end
    end
  end
  
  class ValidationChecker < Struct.new(:field_name, :proc, :testcase, :instance)
    # Assign the given value to the instance
    def assign(value)
      instance.send "#{field_name}=", value
    end

    # Ensure that this value is invalid.
    # If a message is supplied, it will be matched against the validation error message.
    # The message is matched using === so you can use Regexp's.
    def invalid(value, message = nil)
      assign value
      assert ! instance.valid?, "expected #{field_name} = #{value.inspect} to be invalid"
  
      return unless message
      
      found_msg = instance.errors.on(field_name)
      assert message === found_msg, "\nexpected error message: #{message.inspect}\nfound: #{found_msg.inspect}"
    end

    # Ensure that the given value is valid.
    def valid(value)
      assign value
      assert instance.valid?, "expected #{field_name} = #{value.inspect} to be valid"
      assert instance.save # Make sure the database agree's with the validations
    end

    # Run the validation tests
    def run
      assert(instance.valid?, "Initial object ought to be valid!")
      instance_eval(&proc)
    end
  
    def assert
    end
    
    undef_method :assert # Use the testcase's method
  
    # Forward missing methods to the testcase if possible
    def method_missing(sel, *args)
      testcase.respond_to?(sel) ? testcase.send(sel, *args) : super(sel, *args)
    end
  end

  def self.append_features(cls)
    cls.send :include, InstanceMethods
    class << cls
      include ClassMethods
    end
  end
end

Ruby ARTestHelper的例子

#require 'test/unit' unless defined? $ZENTEST and $ZENTEST

require File.dirname(__FILE__) + '/../test_helper'

class AddressObserverTest < Test::Rails::TestCase
  def test_class_instance
    assert 1
  end
end

class AddressTest < Test::Rails::TestCase

  include ARTestHelper

  check_associations %w(account addressable)

  check_validations_for :street_address do
    invalid nil
    invalid 'st'
    invalid 'st'.ljust(200)
    valid 'This is some street address'
  end  
  
  check_validations_for :city do
    invalid nil
    invalid 'ci'
    invalid 'ci'.ljust(200)
    valid 'Funchal'
  end  
  
  check_validations_for :state do
    invalid nil
    invalid 's'
    invalid 's'.ljust(200)
    valid 'Madeira'
    valid 'CA'
  end  
  
  check_validations_for :postal_code do
    invalid nil
    invalid 'p'
    invalid 'p'.ljust(200)
    valid '9000-055'
  end    
  
  def setup
    super
    @address = @rentacar_account.addresses.find(1)
    @a = @address
  end  

  def test_account
    assert_a_account_instance
  end

  def test_account_eh
    assert_raise( ArgumentError){ @address.account? }
  end

  def test_account_equals
    assert_equal @address.account, @rentacar_account
    @address.account = @property_account
    assert_equal @address.account, @property_account
  end

  def test_addressable
    assert_a_addressable_instance
  end

  def test_addressable_equals
    assert_equal @address.addressable, @rentacar_account.branches.find(1)
    new_addressable = valid_branch( :parent => @rentacar_account )
    @address.addressable = new_addressable
    assert_equal @address.addressable, new_addressable
  end

  def test_build_account
    assert_a_account_build
  end

  def test_create_account
    assert_a_account_create
  end

  def test_has_account_eh
    assert @address.has_account?
  end

  def test_set_account_target
    assert_a_account_target
  end

  def test_set_addressable_target
    assert_a_addressable_target
  end
  
  def test_build_country
    assert_instance_of Globalize::Country, @address.build_country
  end

  def test_country
    assert_a_country_instance
  end

  def test_country_eh
    assert_raise( ArgumentError ){@address.country? }
  end

  def test_country_equals
    @address.country = Globalize::Country.find(:first)
    assert_equal Globalize::Country.find(:first), @address.country
  end

  def test_create_country
    assert_a_country_create
  end

  def test_has_country_eh
    assert @address.has_country?
  end

  def test_set_country_target
    assert_a_country_target
  end  
  
end

Ruby 红宝石阵列印章

# for creating sets of data for tables generally. it enables you to
# take an array and create sections with it.
#
# a = [1,2,3,4,5,6,7,8,9,10]
# b = array_chop(a,3)
# b.inspect
# "[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]"

def array_chop( data, num )
  res = Array.new
  buf = data.to_a.reverse
  ( data.size / num ).times do |row|
    tmp = Array.new
    num.times do |col|
      tmp << buf.pop
    end
    res << tmp
  end
  res << buf unless buf.size == 0
  res
end

Ruby ruby数组独特

#!/usr/bin/env ruby

a = [0,1,2,3,4,5,2,3]
p a.select{|e| a.index(e) != a.rindex(e)}.uniq

Ruby 红宝石ascii高尔夫的例子

#!/usr/bin/env ruby

$><<"\e[2J";s,o,f,c,u=20,"#"," ";b,m=(z=0..s).map{z.map{(rand<0.3)?o:f}},[-1,0,1]*3;until c==b;c=b.map{|z|z.dup};$><<"\e[H"<<b.map{|x|x*f}*"";s.times{|i|s.times{|e|n=(m.sort.zip(m)-[[0,0]]).select{|x,z|o==(c[i+x]||[])[e+z]}.size;n!=2?b[i][e]=n==3?o:f:f}};sleep 0.2;end

Ruby ruby添加缺少的++运算符

#!/usr/bin/ruby

class MyFixnum

  def initialize( i )
    @i = i
  end

  def pp
    @i += 1
  end
  
end

a = MyFixnum.new( 7 )
puts a.pp

Ruby Rails活动记录单个错误补丁

# config/environment.rb
require "#{RAILS_ROOT}/app/overrides/all"

# app/overrides/all.rb
Dir[ File.dirname( __FILE__ ) + "/**/*.rb" ].each { |file| require( file ) }

# app/overrides/active_record.rb
module ActiveRecord
  class Errors
    # ONLY RETURN THE 1ST ERROR FOR EACH ATTRIBUTE
    def first_messages
      list = []
      first_messages = []
      @errors.each_key do |attr|
        @errors[ attr ].each do |msg|
          next if msg.nil?
          if attr == 'base'
            unless list.include? attr
              first_messages << msg
              list << attr
            end
          else
            unless list.include? attr
              first_messages << @base.class.human_attribute_name( attr ) + ' ' + msg
              list << attr
            end
          end
        end
      end
      return first_messages
    end
  end
end

Ruby Rails CSS上传器/验证器

#!/usr/bin/env ruby

curl = `which curl 2>/dev/null`.chomp
validator = 'http://jigsaw.w3.org/css-validator/validator'

# All warnings, CSS2, all mediums
options = 'warning=2&profile=css2&usermedium=all'

base = File.expand_path("#{File.dirname(__FILE__)}/../../public/stylesheets")

# Got curl?
raise "Curl not found" if curl.empty?

# Get path to stylesheets
if ARGV.size > 0
  base = ARGV.shift
end

# All css files or just one?
glob = base =~ /css$/ ? base : "#{base}/*.css"

# Do files
Dir.glob(glob) do |file|

  next unless File.exists?( file )

  errors, warnings = [ ], [ ]

  # Send the css file to the validator
  results = `#{curl} -s -F "file=@#{file}" -F "#{options}" #{validator}`

  # Validator couldn't find the file
  #
  # OR the file didn't have _any_ valid css content before
  # the errors <- little gotcha
  #
  results.grep(/No style sheet found/) do |line|
    STDERR << "#{$&}\n"
    exit
  end

  # Add new lines to <li></li> tags so grep can find them easier
  results.gsub!(/\n/,'').gsub!(/<li>/,"\n<li>").gsub!(/<\/li>/,"</li>\n")

  results.grep(/<li>.*<\/li>/) do |line|

    # collect errors
    line.grep(/<span class='error'>/) do |error|
      errors << error.gsub!(/(<p>|<\/p>)/,"\n").gsub!(/<(.|\n)*?>/, '')
    end

  end

  # collect warnings
  results.grep(/<span class='warning'>/) do |line|
    warnings << line.gsub!(/<(.|\n)*?>/, '')
  end

  # Dump information to STDERR
  STDERR << "CSS File #{file}:\n\n"

  { 'Errors' => errors, 'Warnings' => warnings }.each do |k,v|

    if v.empty?
      STDERR << "No #{k.downcase} found\n---------------\n\n"
    else
      STDERR << "#{k} found:\n-------------\n\n"
      v.each {|line| STDERR << line}
    end

  end

end

Ruby Rails alt行颜色

# Used to alternate row colors in a table
def alt( s = '', s2 = '2' )
  @alt_state = true if @alt_state.nil?
  @alt_state = !@alt_state
  @alt_state ? s2 : s
end

Ruby Rails第n个工作日

require "date"
require "date2"

class DateCalc

  ######################################################
  # Returns the date for a specified day on a numbered
  # week as a date object. Such as finding the 3rd Wed 
  # in March, 2006.
  # 
  #   example: nth_weekday(2005,1,2,0)
  #   (2nd Sunday in January 2005) -> Jan 8th, 2005
  #
  # Returns: Date object  
  ######################################################
  def nth_weekday(year,month,week,day)
    test_date = Date.new(year,month,1)
    last_day = Date.new(test_date.year, test_date.month, -1).day
    first_weekday = test_date.wday
    offset = first_weekday - (day % 7)
    weeks = (last_day) / 7
    test_date + (week * 7) - offset
  end
  
end