Adding wkhtmltoimage-amd64

require 'spec_helper'

describe WebSnap::Snapper do

  context "initialization" do
    it "should accept HTML as the source" do
      websnap = WebSnap::Snapper.new('
Oh Hai
')
      websnap.source.should be_html
      websnap.source.to_s.should == '
Oh Hai
'
    end

    it "should accept a URL as the source" do
      websnap = WebSnap::Snapper.new('http://google.com')
      websnap.source.should be_url
      websnap.source.to_s.should == 'http://google.com'
    end

    it "should accept a File as the source" do
      file_path = File.join(SPEC_ROOT,'fixtures','google.html')
      websnap = WebSnap::Snapper.new(File.new(file_path))
      websnap.source.should be_file
      websnap.source.to_s.should == file_path
    end

    it "should parse the options into a cmd line friedly format" do
      websnap = WebSnap::Snapper.new('html', :'crop-w' => '800', :'crop-h' => '600')
      websnap.options.should have_key('--crop-w')
    end

    it "should provide default options" do
      websnap = WebSnap::Snapper.new('
Oh Hai
')
      ['--crop-w', '--crop-h', '--crop-x', '--crop-y', '--format'].each do |option|
        websnap.options.should have_key(option)
      end
    end
  end

  context "command" do
    it "should contstruct the correct command" do
      websnap = WebSnap::Snapper.new('html', :'encoding' => 'Big5')
      websnap.command.should include('--encoding Big5')
    end

    it "read the source from stdin if it is html" do
      websnap = WebSnap::Snapper.new('html')
      websnap.command.should match(/ - -$/)
    end

    it "specify the URL to the source if it is a url" do
      websnap = WebSnap::Snapper.new('http://google.com')
      websnap.command.should match(/ http:\/\/google\.com -$/)
    end

    it "should specify the path to the source if it is a file" do
      file_path = File.join(SPEC_ROOT,'fixtures','google.html')
      websnap = WebSnap::Snapper.new(File.new(file_path))
      websnap.command.should match(/ #{file_path} -$/)
    end
  end

  context "#to_bytes" do
    it "should generate a PDF of the HTML" do
      websnap = WebSnap::Snapper.new('html')
      websnap.expects(:to_bytes).returns('PNG')
      png = websnap.to_bytes
      png.should match(/PNG/)
    end
  end

  context "#to_file" do
    before do
      @file_path = File.join(SPEC_ROOT,'fixtures','test.png')
      File.delete(@file_path) if File.exist?(@file_path)
    end

    after do
      File.delete(@file_path)
    end

    it "should create a file with the PNG as content" do
      websnap = WebSnap::Snapper.new('html')
      websnap.expects(:to_bytes).returns('PNG')

      file = websnap.to_file(@file_path)
      file.should be_instance_of(File)
      File.read(file.path).should == 'PNG'
    end
  end

end

class WkhtmlImageExecutable
  attr_reader :system_platform

  def initialize(args={})
    @system_platform = args[:system_platform] || default_platform
  end

  def default_platform
    RUBY_PLATFORM
  end

  def binary_wkhtmltoimage
    case @system_platform
    when /x86_64-linux/
      'wkhtmltoimage-amd64'
    when /linux/
      'wkhtmltoimage-linux-i386-0.10.0'
    when /darwin/
      'wkhtmltoimage-osx-i386-0.10.0'
    else
      raise "No bundled binaries found for your system. Please install wkhtmltopdf."
    end
  end
end

require 'spec_helper'

describe WebSnap::Snapper do

  context "initialization" do
    it "should accept HTML as the source" do
      websnap = WebSnap::Snapper.new('
Oh Hai
')
      websnap.source.should be_html
      websnap.source.to_s.should == '
Oh Hai
'
    end

    it "should accept a URL as the source" do
      websnap = WebSnap::Snapper.new('http://google.com')
      websnap.source.should be_url
      websnap.source.to_s.should == 'http://google.com'
    end

    it "should accept a File as the source" do
      file_path = File.join(SPEC_ROOT,'fixtures','google.html')
      websnap = WebSnap::Snapper.new(File.new(file_path))
      websnap.source.should be_file
      websnap.source.to_s.should == file_path
    end

    it "should parse the options into a cmd line friedly format" do
      websnap = WebSnap::Snapper.new('html', :'crop-w' => '800', :'crop-h' => '600')
      websnap.options.should have_key('--crop-w')
    end

    it "should provide default options" do
      websnap = WebSnap::Snapper.new('
Oh Hai
')
      ['--format'].each do |option|
        websnap.options.should have_key(option)
      end
    end
  end

  context "command" do
    it "should contstruct the correct command" do
      websnap = WebSnap::Snapper.new('html', :'encoding' => 'Big5')
      websnap.command.should include('--encoding Big5')
    end

    it "read the source from stdin if it is html" do
      websnap = WebSnap::Snapper.new('html')
      websnap.command.should match(/ - -$/)
    end

    it "specify the URL to the source if it is a url" do
      websnap = WebSnap::Snapper.new('http://google.com')
      websnap.command.should match(/ http:\/\/google\.com -$/)
    end

    it "should specify the path to the source if it is a file" do
      file_path = File.join(SPEC_ROOT,'fixtures','google.html')
      websnap = WebSnap::Snapper.new(File.new(file_path))
      websnap.command.should match(/ #{file_path} -$/)
    end
  end

  context "#to_bytes" do
    it "should generate a PDF of the HTML" do
      websnap = WebSnap::Snapper.new('html')
      websnap.expects(:to_bytes).returns('PNG')
      png = websnap.to_bytes
      png.should match(/PNG/)
    end
  end

  context "#to_file" do
    before do
      @file_path = File.join(SPEC_ROOT,'fixtures','test.png')
      File.delete(@file_path) if File.exist?(@file_path)
    end

    after do
      File.delete(@file_path)
    end

    it "should create a file with the PNG as content" do
      websnap = WebSnap::Snapper.new('html')
      websnap.expects(:to_bytes).returns('PNG')

      file = websnap.to_file(@file_path)
      file.should be_instance_of(File)
      File.read(file.path).should == 'PNG'
    end
  end

end

require 'spec_helper'
require_relative '../bin/wkhtml_image_executable'

describe WkhtmlImageExecutable do

  it "should return wkhtmltoimage-amd64 when platform contains 64-linux" do
    WkhtmlImageExecutable.new({system_platform: "x86_64-linux"}).binary_wkhtmltoimage.should eq "wkhtmltoimage-amd64"
  end

  it "should return wkhtmltoimage-linux-i386-0.10.0 when platform contains linux" do
    WkhtmlImageExecutable.new({system_platform: "linux"}).binary_wkhtmltoimage.should eq "wkhtmltoimage-linux-i386-0.10.0"
  end

  it "should return wkhtmltoimage-osx-i386-0.10.0 when platform contains darwin" do
    WkhtmlImageExecutable.new({system_platform: "darwin"}).binary_wkhtmltoimage.should eq "wkhtmltoimage-osx-i386-0.10.0"
  end

  it "should raise execption when platform is not found with a matching " do
    expect {WkhtmlImageExecutable.new({system_platform: "magical_unicorns"}).binary_wkhtmltoimage}.to raise_error
  end
end


Remove lonely tab

#coding=utf-8
"""A set of Observers.
"""
__author__ = (u"Sébastien BARTHÉLEMY <barthelemy@crans.org>",
              "Joseph SALINI <joseph.salini@gmail.com>")
import arboris.core
from abc import ABCMeta, abstractmethod, abstractproperty
from numpy import dot, array, eye, linalg, vstack, hstack, zeros, diag
from time import time as _time
from arboris.massmatrix import principalframe
import logging
logging.basicConfig(level=logging.DEBUG)

class EnergyMonitor(arboris.core.Observer):
    """Compute and store the world energy at each time step.

    **Example:**

        >>> from arboris.core import World, simulate
        >>> from arboris.robots.simplearm import add_simplearm
        >>> w = World()
        >>> observers = [EnergyMonitor()]
        >>> add_simplearm(w)
        >>> simulate(w, [0,1,2], observers)
        >>> #obs.plot()

    """

    def init(self, world, timeline):
        self._world = world
        self.time = []
        self.kinetic_energy = []
        self.potential_energy = []
        self.mechanichal_energy = []
        self._com_pos = {}
        self._bodies = self._world.ground.iter_descendant_bodies
        for body in self._bodies():
            self._com_pos[body]  = principalframe(body.mass)[:,3]

    def update(self, dt):
        self.time.append(self._world.current_time)
        Ec = dot(self._world.gvel,
                 dot(self._world.mass, self._world.gvel) )/2.
        self.kinetic_energy.append(Ec)
        Ep = 0.
        for body in self._bodies():
            h = dot( dot(body.pose, self._com_pos[body])[0:3], self._world.up)
            Ep += body.mass[3,3] * h
        Ep *= 9.81
        self.potential_energy.append(Ep)
        self.mechanichal_energy.append(Ec+Ep)

    def finish(self):
        pass

    def plot(self):
        """Plot the energy evolution.
        """
        from pylab import plot, show, legend, xlabel, ylabel, title
        plot(self.time, self.kinetic_energy)
        plot(self.time, self.potential_energy)
        plot(self.time, self.mechanichal_energy)
        legend(('kinetic','potential','mechanical'))
        title('Energy evolution')
        xlabel('time (s)')
        ylabel('energy (J)')
        show()

class PerfMonitor(arboris.core.Observer):
    """

    **Example:**

        >>> from arboris.core import World, simulate
        >>> from arboris.robots.simplearm import add_simplearm
        >>> w = World()
        >>> obs = PerfMonitor()
        >>> add_simplearm(w)
        >>> simulate(w, [0,1,2], [obs])
        >>> print obs.get_summary() #doctest: +ELLIPSIS
        total computation time (s): ...
        min computation time (s): ...
        mean computation time (s): ...
        max computation time (s): ...
        >>> #obs.plot()

    """
    def __init__(self, log=False):
        arboris.core.Observer.__init__(self)
        if log:
            self._logger = logging.getLogger(self.__class__.__name__)
        else:
            self._logger = None
        self._last_time = None
        self._computation_time = []

    def init(self, world, timeline):
        self._world = world
        self._last_time = _time()

    def update(self, dt):
        current_time = _time()
        self._computation_time.append(current_time - self._last_time)
        self._last_time = current_time
        if self._logger is not None:
            self._logger.info('current time (s): %.3f',
                              self._world.current_time)

    def finish(self):
        pass

    def plot(self):
        from pylab import plot, show, xlabel, ylabel, title
        plot(self._computation_time)
        title('Computation time for each simulation time step')
        xlabel('simulation time step')
        ylabel('computation time (s)')
        show()

    def get_summary(self):
        total = sum(self._computation_time)
        return """total computation time (s): {0}
min computation time (s): {1}
mean computation time (s): {2}
max computation time (s): {3}""".format(
    total,
    min(self._computation_time),
    total/len(self._computation_time),
    max(self._computation_time))

class Hdf5Logger(arboris.core.Observer):
    """An observer that saves the simulation data in an hdf5 file.

    :param filename: name of the output hdf5 file
    :type filename: str
    :param group: subgroup within the hdf5 file. Defaults to "/"
    :type group: str
    :param mode: mode used to open the hdf5 file. Can be 'w' or 'a' (default)
    :type mode: str
    :param save_state: toggle the write of the ``gpos`` and ``gvel`` groups
    :type save_state: bool
    :param save_transforms: toggle the write of the ``transforms`` group
    :type save_transforms: bool
    :param flat: whether to save body of joint poses in the ``transforms`` group
    :type flat: bool
    :param save_model: toggle the write of ``model`` group
    :type save_model: bool

    All the simulation data lies in a single user-chosen group, that is denoted
    ``root`` in the following, and which defaults to ``/``. All the data are in
    S.I. units (radians, meters, newtons and seconds).

    The hdf5 file has the following layout::

      root/timeline (nsteps,)
      root/gpositions/
      root/gvelocities/
      root/model/
      root/transforms/

    The ``gvelocities`` group contains the generalized velocities of the
    joints::

      NameOfJoint0 (nsteps, joint0.ndof)
      NameOfJoint1 (nsteps, joint1.ndof)
      ...

    while the ``gpositions`` group contains their generalized positions.

    The ``model`` group contains the matrices from the dynamical model::

      gvel (nsteps, ndof)
      gforce (nsteps, ndof)
      mass (nsteps, ndof, ndof)
      nleffects (nsteps, ndof, ndof)
      admittance (nsteps, ndof, ndof)

    The ``transforms`` group contains homogeneous transformations,
    useful for viewing an animation of the simulation.

      NameOfTransform0 (nsteps, 4, 4)
      NameOfTransform1 (nsteps, 4, 4)
      ...

    The name and value of the transforms depends on the ``flat`` parameter.
    If ``flat`` is True, then there is one transform per body, named after the
    body and whose value is the body absolute pose (``Body.pose``).
    If ``flat`` is False, there is one transform per joint, whose value is
    ``Joint.pose`` and whose name is taken from the joint second frame
    (``Joint.frames[1].name``).

    """
    def __init__(self, filename, group="/", mode='a', save_state=False,
                 save_transforms=True, flat=False, save_model=False):
        import h5py
        arboris.core.Observer.__init__(self)
        # hdf5 file handlers
        self._file = h5py.File(filename, mode)
        self._root = self._file
        for g in group.split('/'):
            if g:
                self._root = self._root.require_group(g)
        # what to save
        self._save_state = save_state
        self._save_transforms = save_transforms
        self._flat = flat
        self._save_model = save_model

    @property
    def root(self):
        return self._root

    def init(self, world, timeline):
        """Create the datasets.
        """
        self._world = world
        self._nb_steps = len(timeline)-1
        self._current_step = 0
        self._root.require_dataset("timeline", (self._nb_steps,), 'f8')
        if self._save_state:
            self._gpositions = self._root.require_group('gpositions')
            self._gvelocities = self._root.require_group('gvelocities')
            for j in self._world.getjoints():
                self._gpositions.require_dataset(j.name,
                        (self._nb_steps,) + j.gpos.shape[:])
                self._gvelocities.require_dataset(j.name,
                        (self._nb_steps, j.ndof))
        if self._save_transforms:
            self._arb_transforms = {}
            self._transforms = self._root.require_group('transforms')
            if self._flat:
                for b in self._world.iterbodies():
                    self._arb_transforms[b.name] = b
            else:
                for j in  self._world.getjoints():
                    self._arb_transforms[j.frames[1].name] = j
            for f in self._world.itermovingsubframes():
                self._arb_transforms[f.name] = f
            for k in self._arb_transforms.iterkeys():
                d = self._transforms.require_dataset(k,
                                                    (self._nb_steps, 4,4),
                                                    'f8')
        if self._save_model:
            self._model = self._root.require_group('transforms')
            ndof = self._world.ndof
            self._model.require_dataset("gvel",
                    (self._nb_steps, ndof), 'f8')
            self._model.require_dataset("mass",
                    (self._nb_steps, ndof, ndof), 'f8')
            self._model.require_dataset("admittance",
                    (self._nb_steps, ndof, ndof), 'f8')
            self._model.require_dataset("nleffects",
                    (self._nb_steps, ndof, ndof), 'f8')
            self._model.require_dataset("gforce",
                    (self._nb_steps, ndof), 'f8')

    def update(self, dt):
        """Save the current data (state...).
        """
	assert self._current_step <= self._nb_steps
        self._root["timeline"][self._current_step] = self._world._current_time
        if self._save_state:
            for j in self._world.getjoints():
                self._gpositions[j.name][self._current_step] = j.gpos
                self._gvelocities[j.name][self._current_step] = j.gvel
        if self._save_transforms:
            for k, v in self._arb_transforms.iteritems():
                if isinstance(v, arboris.core.MovingSubFrame):
                    if self._flat:
                        pose = v.pose
                    else:
                        pose = v.bpose
                else:
                    pose = v.pose
                self._transforms[k][self._current_step,:,:] = v.pose
        if self._save_model:
            self._model["gvel"][self._current_step,:] = self._world.gvel
            self._model["mass"][self._current_step,:,:] = self._world.mass
            self._model["admittance"][self._current_step,:,:] = \
                    self._world.admittance
            self._model["nleffects"][self._current_step,:,:] = \
                    self._world.nleffects
            self._model["gforce"][self._current_step,:] = self._world.gforce
        self._current_step += 1

    def finish(self):
        self._file.close()


arguments for String#termcolor

# -*- coding: utf-8 -*-
require 'rubygems'
require 'highline'
require 'cgi'
require 'rexml/parsers/streamparser'
require 'rexml/parsers/baseparser'
require 'rexml/streamlistener'

module TermColor
  VERSION = '1.2.1'
  include REXML

  class << self
    def parse(text)
      listener = MyListener.new
      REXML::Parsers::StreamParser.new(prepare_parse(text), listener).parse
      listener.result
    end

    def escape(text)
      escape_or_unescape(:escape, text)
    end

    def unescape(text)
      escape_or_unescape(:unescape, text)
    end

    def escape_or_unescape(dir=:escape, text)
      h = Hash[*%w(& & < < > > ' ' " ")]
      h = h.invert if dir == :unescape
      text.gsub(/(#{h.keys.join('|')})/){ h[$1] }
    end
    private :escape_or_unescape

    def prepare_parse(text)
      tag_separate text.gsub(/<(\/?)(\d+)>/, '<\1_\2>')
    end

    def tag_separate(text)
      re = /<(\/*)([^\W_]+)(?:_(on_[^\W_]+))*(?:_with_([^\W_]+))*(?:_and_([^\W_]+))*>/
      text.gsub(re) do
        matchs = $~.captures
        if matchs.shift.empty?
          tag = ->t{ "<#{t}>" }
        else
          matchs.reverse!
          tag = ->t{ "</#{t}>" }
        end
        matchs.compact.map { |word| tag[word] }.join
      end
    end

    def test(*args)
      args = (0..109).to_a if args.empty?
      args.each_with_index do |color, index|
        print parse("<#{color}> #{color} </#{color}>") + "\t"
        puts if (index + 1) % 10 == 0
      end
    end

    def colorize(text, color)
      parse("<#{color}>#{escape(text)}</#{color}>")
    end
  end

  class MyListener
    include REXML::StreamListener

    attr_reader :result
    def initialize
      @result = ''
      @tag_stack = []
    end

    def tag_start(name, attrs)
      esc_seq = to_esc_seq(name)
      if esc_seq
        @result << esc_seq
        @tag_stack.push(name)
      end
    end

    def text(text)
      @result << CGI.unescapeHTML(text)
    end

    def tag_end(name)
      @tag_stack.pop
      @result << HighLine::CLEAR
      @result << @tag_stack.map{|i| to_esc_seq(i)}.join unless @tag_stack.empty?
    end

    def to_esc_seq(name)
      if (HighLine.const_defined?(name.upcase) rescue false)
        HighLine.const_get(name.upcase)
      else
        case name
        when /^([fb])(\d+)$/
          fb = $1 == 'f' ? 38 : 48
          color = $2.size == 3 ? 16 + $2.to_i(6) : 232 + $2.to_i
          "\e[#{fb};5;#{color}m"
        when /^[^0-9]?(\d+)$/
          "\e[#{$1}m"
        end
      end
    end
  end
end

class String
  def termcolor
    TermColor.parse(self)
  end
end

# -*- coding: utf-8 -*-
require File.dirname(__FILE__) + '/spec_helper'

module TermColor
  describe TermColor do
    before do
    end

    it 'should parse 1' do
      text = TermColor.parse('aaa<red>aaaa<bold>foo</bold>bb<blue>bbbb</blue>bbb</red>ccc<on_yellow>ccccc</on_yellow>ccc')
      puts text
      text.should == "aaa\e[31maaaa\e[1mfoo\e[0m\e[31mbb\e[34mbbbb\e[0m\e[31mbbb\e[0mccc\e[43mccccc\e[0mccc"
    end

    it 'should parse 2' do
      text = TermColor.parse('aa<blue>a<foo>aaa<red>aa</red>aaaa</foo>a</blue>aaa')
      puts text
      text.should == "aa\e[34maaaa\e[31maa\e[0m\e[34maaaa\e[0ma\e[0maaa"
    end

    it 'should parse 3' do
      text = TermColor.parse('aa<blue>aaaaa<aaa"aaa>aaa&aaaaa</blue>aaa')
      puts text
      text.should == "aa\e[34maaaaa<aaa\"aaa>aaa&aaaaa\e[0maaa"
    end

    it 'should parse 4' do
      text = TermColor.parse('aa<30>bbbbbbb<32>cccc<90>ddd</90>c</32>b</30>aaa')
      puts text
      text.should == "aa\e[30mbbbbbbb\e[32mcccc\e[90mddd\e[0m\e[30m\e[32mc\e[0m\e[30mb\e[0maaa"
    end

    it 'should parse 5' do
      text = TermColor.parse('aa<f123>bbbbbbb<b321>ccccc</b321>b</f123>aaa')
      puts text
      text.should == "aa\e[38;5;67mbbbbbbb\e[48;5;137mccccc\e[0m\e[38;5;67mb\e[0maaa"
    end

    it 'should parse 6' do
      text = TermColor.parse('aa<f12>bbbbbbb<b6>ccccc</b6>b</f12>aaa')
      puts text
      text.should == "aa\e[38;5;244mbbbbbbb\e[48;5;238mccccc\e[0m\e[38;5;244mb\e[0maaa"
    end

    it 'should raise Error' do
      lambda{ TermColor.parse('aaaaa<red>aaaaa</blue>aaaaa') }.should raise_error(REXML::ParseException)
      lambda{ TermColor.parse('aaaaa<red>aaaaaaaaaa') }.should_not raise_error(REXML::ParseException)
    end

    it 'should escape text' do
      TermColor.escape('<>&"\'').should == "<>&"'"
    end

    it 'should unescape text' do
      TermColor.unescape("<>&"'").should == '<>&"\''
    end

    it 'should prepare parse' do
      TermColor.prepare_parse("<10>10</10>").should == '<_10>10</_10>'
      TermColor.prepare_parse("<32>10</32>").should == '<_32>10</_32>'
    end

    it 'should convert to escape sequence' do
      listener = TermColor::MyListener.new
      listener.to_esc_seq('red').should == "\e[31m"
      listener.to_esc_seq('on_red').should == "\e[41m"
      listener.to_esc_seq('foo').should == nil
      listener.to_esc_seq('0').should == "\e[0m"
      listener.to_esc_seq('31').should == "\e[31m"
      listener.to_esc_seq('031').should == "\e[031m"
      listener.to_esc_seq('_0').should == "\e[0m"
      listener.to_esc_seq('_31').should == "\e[31m"
    end

    it 'should do colorize' do
      TermColor.colorize('test', :green).should == "\e[32mtest\e[0m"
    end

    it 'should make separate tags for combined-style tag' do
      h = { "<red_on_yellow>hello, world</red_on_yellow>" =>
                   "<red><on_yellow>hello, world</on_yellow></red>",
            "<green_with_bold>hello, world</green_with_bold>" =>
                   "<green><bold>hello, world</bold></green>",
            "<blue_on_white_with_bold_and_underline>hello</blue_on_white_with_bold_and_underline>" =>
                   "<blue><on_white><bold><underline>hello</underline></bold></on_white></blue>",
            "<black_on_white>hello</black_on_white>term<green_with_bold>color</green_with_bold>" =>
                   "<black><on_white>hello</on_white></black>term<green><bold>color</bold></green>" }
      h.each_pair do |combined, separated|
        TermColor.prepare_parse(combined).should == separated
      end
    end
  end
end

# -*- coding: utf-8 -*-
require 'rubygems'
require 'highline'
require 'cgi'
require 'rexml/parsers/streamparser'
require 'rexml/parsers/baseparser'
require 'rexml/streamlistener'

module TermColor
  VERSION = '1.2.1'
  include REXML

  class << self
    def parse(text)
      listener = MyListener.new
      REXML::Parsers::StreamParser.new(prepare_parse(text), listener).parse
      listener.result
    end

    def escape(text)
      escape_or_unescape(:escape, text)
    end

    def unescape(text)
      escape_or_unescape(:unescape, text)
    end

    def escape_or_unescape(dir=:escape, text)
      h = Hash[*%w(& & < < > > ' ' " ")]
      h = h.invert if dir == :unescape
      text.gsub(/(#{h.keys.join('|')})/){ h[$1] }
    end
    private :escape_or_unescape

    def prepare_parse(text)
      tag_separate text.gsub(/<(\/?)(\d+)>/, '<\1_\2>')
    end

    def tag_separate(text)
      re = /<(\/*)([^\W_]+)(?:_(on_[^\W_]+))*(?:_with_([^\W_]+))*(?:_and_([^\W_]+))*>/
      text.gsub(re) do
        matchs = $~.captures
        if matchs.shift.empty?
          tag = ->t{ "<#{t}>" }
        else
          matchs.reverse!
          tag = ->t{ "</#{t}>" }
        end
        matchs.compact.map { |word| tag[word] }.join
      end
    end

    def test(*args)
      args = (0..109).to_a if args.empty?
      args.each_with_index do |color, index|
        print parse("<#{color}> #{color} </#{color}>") + "\t"
        puts if (index + 1) % 10 == 0
      end
    end

    def colorize(text, color)
      parse("<#{color}>#{escape(text)}</#{color}>")
    end
  end

  class MyListener
    include REXML::StreamListener

    attr_reader :result
    def initialize
      @result = ''
      @tag_stack = []
    end

    def tag_start(name, attrs)
      esc_seq = to_esc_seq(name)
      if esc_seq
        @result << esc_seq
        @tag_stack.push(name)
      end
    end

    def text(text)
      @result << CGI.unescapeHTML(text)
    end

    def tag_end(name)
      @tag_stack.pop
      @result << HighLine::CLEAR
      @result << @tag_stack.map{|i| to_esc_seq(i)}.join unless @tag_stack.empty?
    end

    def to_esc_seq(name)
      if (HighLine.const_defined?(name.upcase) rescue false)
        HighLine.const_get(name.upcase)
      else
        case name
        when /^([fb])(\d+)$/
          fb = $1 == 'f' ? 38 : 48
          color = $2.size == 3 ? 16 + $2.to_i(6) : 232 + $2.to_i
          "\e[#{fb};5;#{color}m"
        when /^[^0-9]?(\d+)$/
          "\e[#{$1}m"
        end
      end
    end
  end
end

class String
  def termcolor(color=nil, target=nil)
    tagging = ->s{ "<#{color}>#{s}</#{color}>" }
    str =
      if color
        case target
        when Range
          dself = self.dup
          dself[target] = tagging[(dself[target])]
          dself
        when String, Symbol
          self.gsub(/#{target.to_s}/) { tagging[$&] }
        else
          tagging[self]
        end
      else
        self
      end

    TermColor.parse(str)
  end
end

# -*- coding: utf-8 -*-
require File.dirname(__FILE__) + '/spec_helper'

module TermColor
  describe TermColor do
    before do
    end

    it 'should parse 1' do
      text = TermColor.parse('aaa<red>aaaa<bold>foo</bold>bb<blue>bbbb</blue>bbb</red>ccc<on_yellow>ccccc</on_yellow>ccc')
      puts text
      text.should == "aaa\e[31maaaa\e[1mfoo\e[0m\e[31mbb\e[34mbbbb\e[0m\e[31mbbb\e[0mccc\e[43mccccc\e[0mccc"
    end

    it 'should parse 2' do
      text = TermColor.parse('aa<blue>a<foo>aaa<red>aa</red>aaaa</foo>a</blue>aaa')
      puts text
      text.should == "aa\e[34maaaa\e[31maa\e[0m\e[34maaaa\e[0ma\e[0maaa"
    end

    it 'should parse 3' do
      text = TermColor.parse('aa<blue>aaaaa<aaa"aaa>aaa&aaaaa</blue>aaa')
      puts text
      text.should == "aa\e[34maaaaa<aaa\"aaa>aaa&aaaaa\e[0maaa"
    end

    it 'should parse 4' do
      text = TermColor.parse('aa<30>bbbbbbb<32>cccc<90>ddd</90>c</32>b</30>aaa')
      puts text
      text.should == "aa\e[30mbbbbbbb\e[32mcccc\e[90mddd\e[0m\e[30m\e[32mc\e[0m\e[30mb\e[0maaa"
    end

    it 'should parse 5' do
      text = TermColor.parse('aa<f123>bbbbbbb<b321>ccccc</b321>b</f123>aaa')
      puts text
      text.should == "aa\e[38;5;67mbbbbbbb\e[48;5;137mccccc\e[0m\e[38;5;67mb\e[0maaa"
    end

    it 'should parse 6' do
      text = TermColor.parse('aa<f12>bbbbbbb<b6>ccccc</b6>b</f12>aaa')
      puts text
      text.should == "aa\e[38;5;244mbbbbbbb\e[48;5;238mccccc\e[0m\e[38;5;244mb\e[0maaa"
    end

    it 'should raise Error' do
      lambda{ TermColor.parse('aaaaa<red>aaaaa</blue>aaaaa') }.should raise_error(REXML::ParseException)
      lambda{ TermColor.parse('aaaaa<red>aaaaaaaaaa') }.should_not raise_error(REXML::ParseException)
    end

    it 'should escape text' do
      TermColor.escape('<>&"\'').should == "<>&"'"
    end

    it 'should unescape text' do
      TermColor.unescape("<>&"'").should == '<>&"\''
    end

    it 'should prepare parse' do
      TermColor.prepare_parse("<10>10</10>").should == '<_10>10</_10>'
      TermColor.prepare_parse("<32>10</32>").should == '<_32>10</_32>'
    end

    it 'should convert to escape sequence' do
      listener = TermColor::MyListener.new
      listener.to_esc_seq('red').should == "\e[31m"
      listener.to_esc_seq('on_red').should == "\e[41m"
      listener.to_esc_seq('foo').should == nil
      listener.to_esc_seq('0').should == "\e[0m"
      listener.to_esc_seq('31').should == "\e[31m"
      listener.to_esc_seq('031').should == "\e[031m"
      listener.to_esc_seq('_0').should == "\e[0m"
      listener.to_esc_seq('_31').should == "\e[31m"
    end

    it 'should do colorize' do
      TermColor.colorize('test', :green).should == "\e[32mtest\e[0m"
    end

    it 'should make separate tags for combined-style tag' do
      h = { "<red_on_yellow>hello, world</red_on_yellow>" =>
                   "<red><on_yellow>hello, world</on_yellow></red>",
            "<green_with_bold>hello, world</green_with_bold>" =>
                   "<green><bold>hello, world</bold></green>",
            "<blue_on_white_with_bold_and_underline>hello</blue_on_white_with_bold_and_underline>" =>
                   "<blue><on_white><bold><underline>hello</underline></bold></on_white></blue>",
            "<black_on_white>hello</black_on_white>term<green_with_bold>color</green_with_bold>" =>
                   "<black><on_white>hello</on_white></black>term<green><bold>color</bold></green>" }
      h.each_pair do |combined, separated|
        TermColor.prepare_parse(combined).should == separated
      end
    end

    it 'should do colorize using String#termcolor' do
      "<red>ruby</red>".termcolor.should == "\e[31mruby\e[0m"
      "ruby".termcolor(:red).should == "<red>ruby</red>".termcolor
      "ruby isn't ruby gem".termcolor(:red, 11..14).should == "ruby isn't <red>ruby</red> gem".termcolor
      "ruby isn't ruby gem".termcolor(:red, 'ruby').should == "<red>ruby</red> isn't <red>ruby</red> gem".termcolor
      "ruby isn't ruby gem".termcolor(:red, :ruby).should == "<red>ruby</red> isn't <red>ruby</red> gem".termcolor
      "Ruby isn't Ruby Gem".termcolor(:red, '[A-Z]+').should == "<red>R</red>uby isn't <red>R</red>uby <red>G</red>em".termcolor
    end
  end
end


Prevent dotaccess lib from throwing exception in browser.

module.exports.set = set
module.exports.unset = unset
module.exports.get = get

function parts(key) {
  if (Array.isArray(key)) return key
  return key.split('.')
}

function lookup(obj, key) {
  key = parts(key)
  var lastKey = key.pop()
  for (var i=0, l=key.length; i<l; i++) {
    var part = key[i]
    if (!(part in obj)) obj[part] = {}
    obj = obj[part]
    if (!obj) throw new Error('dotaccess: incompatible value in ' + part)
  }
  return [obj, lastKey];
}

function set(obj, key, value, overwrite) {
  var objectAndKey = lookup(obj, key)
    , obj = objectAndKey[0]
    , key = objectAndKey[1];
  if (overwrite || !(key in obj)) obj[key] = value
}

function unset(obj, key) {
  var objectAndKey = lookup(obj, key)
    , obj = objectAndKey[0]
    , key = objectAndKey[1];
  return delete obj[key];
}

function get(obj, key, def) {
  key = parts(key)
  for (var i=0, l=key.length; i<l; i++) {
    var part = key[i]
    if (!(part in obj)) return def
    obj = obj[part]
  }
  return obj
}

var dotaccess = {
  set: set,
  unset: unset,
  get: get
}

if (typeof module != 'undefined' && module.exports) {
  module.exports = dotaccess;
}

function parts(key) {
  if (Array.isArray(key)) return key
  return key.split('.')
}

function lookup(obj, key) {
  key = parts(key)
  var lastKey = key.pop()
  for (var i=0, l=key.length; i<l; i++) {
    var part = key[i]
    if (!(part in obj)) obj[part] = {}
    obj = obj[part]
    if (!obj) throw new Error('dotaccess: incompatible value in ' + part)
  }
  return [obj, lastKey];
}

function set(obj, key, value, overwrite) {
  var objectAndKey = lookup(obj, key)
    , obj = objectAndKey[0]
    , key = objectAndKey[1];
  if (overwrite || !(key in obj)) obj[key] = value
}

function unset(obj, key) {
  var objectAndKey = lookup(obj, key)
    , obj = objectAndKey[0]
    , key = objectAndKey[1];
  return delete obj[key];
}

function get(obj, key, def) {
  key = parts(key)
  for (var i=0, l=key.length; i<l; i++) {
    var part = key[i]
    if (!(part in obj)) return def
    obj = obj[part]
  }
  return obj
}


Fixes for Rails 4.2 compatibility.

# Describes an attribute as being unique, possibly within a certain scope.
#
# ==Options
# * :scope [array] - a list of other fields that define the context for uniqueness. it's like defining a multi-column uniqueness constraint.
# * :case_sensitive [boolean, default false] - whether case matters for uniqueness.
#
# ==Examples
#   field_is_unique :case_sensitive => true
#   field_is_unique :scope => [:other_field, :another_other_field]
class Predicates::Unique < Predicates::Base
  attr_accessor :case_sensitive
  attr_accessor :scope

  def initialize(attribute, options = {})
    defaults = {
      :scope => [],
      :case_sensitive => false
    }
    super attribute, defaults.merge(options)
  end

  def error_message
    @error_message || :taken
  end

  def validate(value, record)
    klass = record.class

    # merge all the scope fields with this one. they must all be unique together.
    # no special treatment -- case sensitivity applies to all or none.
    values = [scope].flatten.collect{ |attr| [attr, record.send(attr)] }
    values << [@attribute, value]

    sql = values.map do |(attr, attr_value)|
      comparison_for(attr, attr_value, klass)
    end

    unless record.new_record?
      sql << klass.send(:sanitize_sql, ["#{klass.quoted_table_name}.#{klass.primary_key} <> ?", record.id])
    end

    !klass.where(sql.join(" AND ")).exists?
  end

  protected

  def comparison_for(field, value, klass)
    quoted_field = "#{klass.quoted_table_name}.#{klass.connection.quote_column_name(field)}"

    if klass.columns_hash[field.to_s].text?
      if case_sensitive
        # case sensitive text comparison in any database
        klass.send(:sanitize_sql, ["#{quoted_field} #{klass.connection.case_sensitive_equality_operator} ?", value])
      elsif mysql?(klass.connection)
        # case INsensitive text comparison in mysql - yes this is a database specific optimization. i'm always open to better ways. :)
        klass.send(:sanitize_sql, ["#{quoted_field} = ?", value])
      else
        # case INsensitive text comparison in most databases
        klass.send(:sanitize_sql, ["LOWER(#{quoted_field}) = ?", value.to_s.downcase])
      end
    else
      # non-text comparison
      klass.send(:sanitize_sql, {field => value})
    end
  end

  def mysql?(connection)
    (defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) and connection.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)) or
      (defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) and connection.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter))
  end
end

# active record tie-ins
module SemanticAttributes
  module Predicates
    def self.included(base)
      base.class_eval do
        extend ClassMethods
        base.semantic_attributes = SemanticAttributes::Set.new
        attribute_method_suffix '_valid?'
        validate :validate_predicates
      end
    end

    def semantic_attributes
      self.class.semantic_attributes
    end

    # the validation hook that checks all predicates
    def validate_predicates
      semantic_attributes.each do |attribute|
        applicable_predicates = attribute.predicates.select{|p| validate_predicate?(p)}

        next if applicable_predicates.empty?

        # skip validations on associations that aren't already loaded
        # note: it's not worth skipping a has_one, since there's no way to distinguish
        # between loaded and absent without a query.
        if reflection = self.class.reflect_on_association(attribute.field.to_sym)
          if reflection.collection?
            next unless self.association(attribute.field).loaded?
          elsif reflection.macro == :belongs_to and self[reflection.foreign_key]
            next unless self.association(attribute.field).loaded?
          end
        end

        value = self.send(attribute.field)
        applicable_predicates.each do |predicate|
          if value.blank?
            # it's empty, so add an error or not but either way move along
            self.errors.add(attribute.field, predicate.error) unless predicate.allow_empty?
            next
          end

          unless predicate.validate(value, self)
            self.errors.add(attribute.field, predicate.error)
          end
        end
      end
    end

    protected
    # Returns true if this attribute would pass validation during the next save.
    # Intended to be called via attribute suffix, like:
    #   User#login_valid?
    def attribute_valid?(attr)
      semantic_attributes[attr.to_sym].predicates.all? do |p|
        !validate_predicate?(p) or (self.send(attr).blank? and p.allow_empty?) or p.validate(self.send(attr), self)
      end
    end

    # Returns true if the given predicate (for a specific attribute) should be validated.
    def validate_predicate?(predicate)
      case predicate.validate_if
        when Symbol
        return false unless send(predicate.validate_if)

        when Proc
        return false unless predicate.validate_if.call(self)
      end

      case predicate.validate_on
        when :create
        return false unless new_record?

        when :update
        return false if new_record?
      end

      true
    end

    module ClassMethods
      def semantic_attributes
        @semantic_attributes ||= SemanticAttributes::Set.new
      end

      def semantic_attributes=(val)
        @semantic_attributes = val || SemanticAttributes::Set.new
      end

      def inherited(klass)
        klass.semantic_attributes = self.semantic_attributes.dup
        super
      end

      # Provides sugary syntax for adding and querying predicates
      #
      # The syntax supports the following forms:
      #   #{attribute}_is_#{predicate}(options = {})
      #   #{attribute}_is_a_#{predicate}(options = {})
      #   #{attribute}_is_an_#{predicate}(options = {})
      #   #{attribute}_has_#{predicate}(options = {})
      #   #{attribute}_has_a_#{predicate}(options = {})
      #   #{attribute}_has_an_#{predicate}(options = {})
      #
      # You may also add required-ness into the declaration:
      #   #{attribute}_is_a_required_#{predicate}(options = {})
      #
      # If you want to assign a predicate to multiple fields, you may replace the attribute component with the word 'fields', and pass a field list as the first argument, like this:
      #   fields_are_#{predicate}(fields = [], options = {})
      #
      # Each form may also have a question mark at the end, to query whether the attribute has the predicate.
      #
      # In order to avoid clashing with other method_missing setups, this syntax is checked *last*, after all other method_missing metaprogramming attempts have failed.
      def method_missing(name, *args)
        begin
          super
        rescue NameError
          if /^(.*)_(is|has|are)_(an?_)?(required_)?([^?]*)(\?)?$/.match(name.to_s)
            options = args.last.is_a?(Hash) ? args.pop : {}
            options[:or_empty] = false if !$4.nil?
            fields = ($1 == 'fields') ? args.map(&:to_s) : [$1]

            predicate = $5
            if $6 == '?'
              self.semantic_attributes[fields.first].has? predicate
            else
              args = [predicate]
              args << options if options
              fields.each do |field|
                # TODO: create a less sugary method that may be used programmatically and takes care of defining the normalization method properly
                define_humanization_method_for field
                define_normalization_method_for field
                self.semantic_attributes[field].add *args
              end
            end
          else
            raise
          end
        end
      end

      # Provides a way to pre-validate a single value out of context of
      # an entire record. This is helpful for validating parts of a form
      # before it has been submitted.
      #
      # For values that are (in)valid only in context, such as the common
      # :password_confirmation (which is only valid with a matching :password),
      # additional values may be specified.
      #
      # WARNING: I still have not figured out what to do about differences in
      # validation for new/existing records.
      #
      # Returns first error message if value is expected invalid.
      #
      # Example:
      #   User.expected_error_for(:username, "bob")
      #   => "has already been taken."
      #   User.expected_error_for(:username, "bob2392")
      #   => nil
      #   User.expected_error_for(:password_confirmation, "mismatched", :password => "opensesame")
      #   => "must be the same as password."
      def expected_error_for(attribute, value, extra_values = {})
        @record = self.new(extra_values)
        semantic_attributes[attribute.to_sym].predicates.each do |predicate|
          return predicate.error_message unless predicate.validate(value, @record)
        end
        nil
      end
    end
  end
end

ENV["RAILS_ENV"] = "test"

# load the support libraries
require 'minitest/autorun'
require 'rubygems'
require 'active_record'
require 'active_record/fixtures'
require 'active_support/time'

# load the code-to-be-tested
require 'semantic_attributes'

Time.zone = 'UTC'

# establish the database connection
ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/db/database.yml'))
ActiveRecord::Base.establish_connection(:semantic_attributes_test)

# capture the logging
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/test.log")

# load the schema ... silently
ActiveRecord::Migration.verbose = false
load(File.dirname(__FILE__) + "/db/schema.rb")

# load the ActiveRecord models
require File.dirname(__FILE__) + '/db/models'

# configure the TestCase settings
class SemanticAttributes::TestCase < ActiveSupport::TestCase
  include ActiveRecord::TestFixtures
  include PluginTestModels

  self.use_transactional_fixtures = true
  self.use_instantiated_fixtures  = false
  self.fixture_path = File.dirname(__FILE__) + '/fixtures/'

  fixtures :all
end

class ActiveRecord::Base
  # Aids the management of per-test semantics.
  #
  # Examples:
  #
  # User.stub_semantics_with(:email => :email)
  # User.stub_semantics_with(:email => [:email, :unique])
  # User.stub_semantics_with(:email => {:length => {:above => 5}})
  # User.stub_semantics_with(:email => [:email, {:length => {:above => 5}}])
  def self.stub_semantics_with(attr_predicates = {})
    semantics = SemanticAttributes::Set.new
    attr_predicates.each do |attr, predicates|
      [predicates].flatten.each do |predicate|
        case predicate
          when String, Symbol
          semantics[attr].add(predicate)
          when Hash
          semantics[attr].add(predicate.keys.first, predicate.values.first)
          else
          raise '???'
        end
      end
    end

    self.stubs(:semantic_attributes).returns(semantics)
  end
end

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

class ValidationsTest < SemanticAttributes::TestCase
  def setup
    User.stub_semantics_with(:login => :required)
    @record = User.new
    @login_required = @record.semantic_attributes['login'].get('required')
  end

  def test_validation_hook
    @record.expects(:validate_predicates)
    @record.valid?
  end

  def test_validation_errors
    assert !@record.valid?
    assert @record.errors[:login]
    assert_equal ['is required.'], @record.errors[:login]
  end

  def test_validate_on_default
    assert_equal :both, @record.semantic_attributes['login'].get('required').validate_on
  end

  ##
  ## attr_valid?
  ##

  def test_attribute_valid?
    assert !@record.login_valid?
    @record.login = "foo"
    assert @record.login_valid?
  end

  def test_attribute_valid_with_conditional_validation
    assert !@record.login_valid?
    @record.stubs(:validate_predicate?).returns(false)
    assert @record.login_valid?
  end

  ##
  ## validate_predicate?
  ##

  def test_validate_predicate_with_validate_if_symbol
    @record.stubs(:no).returns(false)
    @record.stubs(:yes).returns(true)

    assert @record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = :no
    assert !@record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = :yes
    assert @record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_if_proc
    assert @record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = proc {false}
    assert !@record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = proc {true}
    assert @record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_on_create
    @record = users(:george)
    @record.login = nil

    # test assumptions
    assert !@record.new_record?
    assert @record.send(:validate_predicate?, @login_required)

    # the test
    @record.semantic_attributes['login'].get('required').validate_on = :create
    assert !@record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_on_update
    # test assumptions
    assert @record.new_record?
    assert @record.send(:validate_predicate?, @login_required)

    # the test
    @record.semantic_attributes['login'].get('required').validate_on = :update
    assert !@record.send(:validate_predicate?, @login_required)
  end

  ##
  ## :or_empty
  ##

  def test_allow_empty
    User.stub_semantics_with(:login => {:number => {:or_empty => true}})

    # empty values should skip the validate method
    predicate = @record.semantic_attributes['login'].get('number')
    predicate.expects(:validate).never

    [nil, '', []].each do |empty_value|
      @record.login = empty_value
      assert @record.valid?
    end
  end

  def test_disallow_empty
    User.stub_semantics_with(:login => {:number => {:or_empty => false}})

    # empty values should skip the validate method
    predicate = @record.semantic_attributes['login'].get('number')
    predicate.expects(:validate).never

    [nil, '', []].each do |empty_value|
      @record.login = empty_value
      assert !@record.valid?
      assert @record.errors[:login]
    end
  end
end

# Describes an attribute as being unique, possibly within a certain scope.
#
# ==Options
# * :scope [array] - a list of other fields that define the context for uniqueness. it's like defining a multi-column uniqueness constraint.
# * :case_sensitive [boolean, default false] - whether case matters for uniqueness.
#
# ==Examples
#   field_is_unique :case_sensitive => true
#   field_is_unique :scope => [:other_field, :another_other_field]
class Predicates::Unique < Predicates::Base
  attr_accessor :case_sensitive
  attr_accessor :scope

  def initialize(attribute, options = {})
    defaults = {
      :scope => [],
      :case_sensitive => false
    }
    super attribute, defaults.merge(options)
  end

  def error_message
    @error_message || :taken
  end

  def validate(value, record)
    klass = record.class

    # merge all the scope fields with this one. they must all be unique together.
    # no special treatment -- case sensitivity applies to all or none.
    values = [scope].flatten.collect{ |attr| [attr, record.send(attr)] }
    values << [@attribute, value]

    sql = values.map do |(attr, attr_value)|
      comparison_for(attr, attr_value, klass)
    end

    unless record.new_record?
      sql << klass.send(:sanitize_sql, ["#{klass.quoted_table_name}.#{klass.primary_key} <> ?", record.id])
    end

    !klass.where(sql.join(" AND ")).exists?
  end

  protected

  def comparison_for(field, value, klass)
    quoted_field = "#{klass.quoted_table_name}.#{klass.connection.quote_column_name(field)}"

    if klass.columns_hash[field.to_s].text?
      if case_sensitive
        # case sensitive text comparison in any database
        if klass.connection.respond_to?(:case_sensitive_equality_operator)
          # <= Rails 4.1
          comparison = klass.send(:sanitize_sql, ["#{quoted_field} #{klass.connection.case_sensitive_equality_operator} ?", value])
        else
          # >= Rails 4.2
          case_sensitive_value = klass.connection.case_sensitive_modifier(value, field)
          case_sensitive_value = case_sensitive_value.to_sql if case_sensitive_value.respond_to?(:to_sql)
          klass.send(:sanitize_sql, ["#{quoted_field} = ?", case_sensitive_value])
        end
      elsif mysql?(klass.connection)
        # case INsensitive text comparison in mysql - yes this is a database specific optimization. i'm always open to better ways. :)
        klass.send(:sanitize_sql, ["#{quoted_field} = ?", value])
      else
        # case INsensitive text comparison in most databases
        klass.send(:sanitize_sql, ["LOWER(#{quoted_field}) = ?", value.to_s.downcase])
      end
    else
      # non-text comparison
      klass.send(:sanitize_sql, ["#{quoted_field} = ?", value])
    end
  end

  def mysql?(connection)
    (defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) and connection.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)) or
      (defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) and connection.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter))
  end
end

ENV["RAILS_ENV"] = "test"

# load the support libraries
require 'rubygems'
require "minitest/autorun"
require "mocha/mini_test"
require 'active_record'
require 'active_record/fixtures'
require 'active_support/time'

# load the code-to-be-tested
require 'semantic_attributes'

Time.zone = 'UTC'

# establish the database connection
ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/db/database.yml'))
ActiveRecord::Base.establish_connection(:semantic_attributes_test)

# capture the logging
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/test.log")

# load the schema ... silently
ActiveRecord::Migration.verbose = false
load(File.dirname(__FILE__) + "/db/schema.rb")

# load the ActiveRecord models
require File.dirname(__FILE__) + '/db/models'

# configure the TestCase settings
class SemanticAttributes::TestCase < ActiveSupport::TestCase
  include ActiveRecord::TestFixtures
  include PluginTestModels

  self.use_transactional_fixtures = true
  self.use_instantiated_fixtures  = false
  self.fixture_path = File.dirname(__FILE__) + '/fixtures/'

  fixtures :all

  # >= Rails 4.2
  if ActiveSupport.respond_to?(:test_order)
    ActiveSupport.test_order = :random
  end

end

class ActiveRecord::Base
  # Aids the management of per-test semantics.
  #
  # Examples:
  #
  # User.stub_semantics_with(:email => :email)
  # User.stub_semantics_with(:email => [:email, :unique])
  # User.stub_semantics_with(:email => {:length => {:above => 5}})
  # User.stub_semantics_with(:email => [:email, {:length => {:above => 5}}])
  def self.stub_semantics_with(attr_predicates = {})
    semantics = SemanticAttributes::Set.new
    attr_predicates.each do |attr, predicates|
      [predicates].flatten.each do |predicate|
        case predicate
          when String, Symbol
          semantics[attr].add(predicate)
          when Hash
          semantics[attr].add(predicate.keys.first, predicate.values.first)
          else
          raise '???'
        end
      end
    end

    self.stubs(:semantic_attributes).returns(semantics)
  end
end

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

class ValidationsTest < SemanticAttributes::TestCase
  def setup
    User.stub_semantics_with(:login => :required)
    @record = User.new
    @login_required = @record.semantic_attributes['login'].get('required')
  end

  def test_validation_hook
    @record.expects(:validate_predicates)
    @record.valid?
  end

  def test_validation_errors
    assert !@record.valid?
    assert @record.errors[:login]
    assert_equal ['is required.'], @record.errors[:login]
  end

  def test_validate_on_default
    assert_equal :both, @record.semantic_attributes['login'].get('required').validate_on
  end

  ##
  ## attr_valid?
  ##

  def test_attribute_valid?
    assert !@record.login_valid?
    @record.login = "foo"
    assert @record.login_valid?
  end

  def test_attribute_valid_with_conditional_validation
    assert !@record.login_valid?
    @record.stubs(:validate_predicate?).returns(false)
    assert @record.login_valid?
  end

  ##
  ## validate_predicate?
  ##

  def test_validate_predicate_with_validate_if_symbol
    @record.stubs(:no).returns(false)
    @record.stubs(:yes).returns(true)

    assert @record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = :no
    assert !@record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = :yes
    assert @record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_if_proc
    assert @record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = proc {false}
    assert !@record.send(:validate_predicate?, @login_required)

    @record.semantic_attributes['login'].get('required').validate_if = proc {true}
    assert @record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_on_create
    @record = users(:george)
    @record.login = nil

    # test assumptions
    assert !@record.new_record?
    assert @record.send(:validate_predicate?, @login_required)

    # the test
    @record.semantic_attributes['login'].get('required').validate_on = :create
    assert !@record.send(:validate_predicate?, @login_required)
  end

  def test_validate_predicate_with_validate_on_update
    # test assumptions
    assert @record.new_record?
    assert @record.send(:validate_predicate?, @login_required)

    # the test
    @record.semantic_attributes['login'].get('required').validate_on = :update
    assert !@record.send(:validate_predicate?, @login_required)
  end

  ##
  ## :or_empty
  ##

  def test_allow_empty
    User.stub_semantics_with(:login => {:number => {:or_empty => true}})

    # empty values should skip the validate method
    predicate = @record.semantic_attributes['login'].get('number')
    predicate.expects(:validate).never

    [nil, ''].each do |empty_value|
      @record.login = empty_value
      assert @record.valid?
    end
  end

  def test_disallow_empty
    User.stub_semantics_with(:login => {:number => {:or_empty => false}})

    # empty values should skip the validate method
    predicate = @record.semantic_attributes['login'].get('number')
    predicate.expects(:validate).never

    [nil, ''].each do |empty_value|
      @record.login = empty_value
      assert !@record.valid?
      assert @record.errors[:login]
    end
  end
end


Django 1.4 compatibility

import os
import codecs
from datetime import datetime
from stat import ST_MTIME, ST_CTIME
from re import search

from django.conf import settings
from django.contrib.auth.decorators import login_required, user_passes_test
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.template.loaders.app_directories import app_template_dirs
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache

from templatesadmin.forms import TemplateForm
from templatesadmin import TemplatesAdminException

# Default settings that may be overriden by global settings (settings.py)
TEMPLATESADMIN_VALID_FILE_EXTENSIONS = getattr(
    settings,
    'TEMPLATESADMIN_VALID_FILE_EXTENSIONS',
    ('html', 'htm', 'txt', 'css', 'backup',)
)

TEMPLATESADMIN_GROUP = getattr(
    settings,
    'TEMPLATESADMIN_GROUP',
    'TemplateAdmins'
)

TEMPLATESADMIN_EDITHOOKS = getattr(
    settings,
    'TEMPLATESADMIN_EDITHOOKS',
    ('templatesadmin.edithooks.dotbackupfiles.DotBackupFilesHook', )
)

TEMPLATESADMIN_HIDE_READONLY = getattr(
    settings,
    'TEMPLATESADMIN_HIDE_READONLY',
    False
)

if str == type(TEMPLATESADMIN_EDITHOOKS):
    TEMPLATESADMIN_EDITHOOKS = (TEMPLATESADMIN_EDITHOOKS,)

_hooks = []

for path in TEMPLATESADMIN_EDITHOOKS:
    # inspired by django.template.context.get_standard_processors
    i = path.rfind('.')
    module, attr = path[:i], path[i+1:]
    try:
        mod = __import__(module, {}, {}, [attr])
    except ImportError, e:
        raise ImproperlyConfigured('Error importing edithook module %s: "%s"' % (module, e))
    try:
        func = getattr(mod, attr)
    except AttributeError:
        raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))

    _hooks.append(func)

TEMPLATESADMIN_EDITHOOKS = tuple(_hooks)

_fixpath = lambda path: os.path.abspath(os.path.normpath(path))

TEMPLATESADMIN_TEMPLATE_DIRS = getattr(
    settings,
    'TEMPLATESADMIN_TEMPLATE_DIRS', [
        d for d in list(settings.TEMPLATE_DIRS) + \
        list(app_template_dirs) if os.path.isdir(d)
    ]
)

TEMPLATESADMIN_TEMPLATE_DIRS = [_fixpath(dir) for dir in TEMPLATESADMIN_TEMPLATE_DIRS]

def user_in_templatesadmin_group(user):
    try:
        user.groups.get(name=TEMPLATESADMIN_GROUP)
        return True
    except ObjectDoesNotExist:
        return False

@never_cache
@user_passes_test(lambda u: user_in_templatesadmin_group(u))
@login_required
def listing(request,
             template_name='templatesadmin/overview.html',
             available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS):

    template_dict = []
    for templatedir in available_template_dirs:
        for root, dirs, files in os.walk(templatedir):
            for f in sorted([f for f in files if f.rsplit('.')[-1] \
                      in TEMPLATESADMIN_VALID_FILE_EXTENSIONS]):
                full_path = os.path.join(root, f)
                l = {
                     'templatedir': templatedir,
                     'rootpath': root,
                     'abspath': full_path,
                     'modified': datetime.fromtimestamp(os.stat(full_path)[ST_MTIME]),
                     'created': datetime.fromtimestamp(os.stat(full_path)[ST_CTIME]),
                     'writeable': os.access(full_path, os.W_OK)
                }

                # Do not fetch non-writeable templates if settings set.
                if (TEMPLATESADMIN_HIDE_READONLY == True and \
                    l['writeable'] == True) or \
                   TEMPLATESADMIN_HIDE_READONLY == False:
                    try:
                        template_dict += (l,)
                    except KeyError:
                        template_dict = (l,)

    template_context = {
        'messages': request.user.get_and_delete_messages(),
        'template_dict': template_dict,
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
    }

    return render_to_response(template_name, template_context,
                              RequestContext(request))
@never_cache
@user_passes_test(lambda u: user_in_templatesadmin_group(u))
@login_required
def modify(request,
           path,
           template_name='templatesadmin/edit.html',
           base_form=TemplateForm,
           available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS):

    template_path = _fixpath(path)

    # Check if file is within template-dirs
    if not any([template_path.startswith(templatedir) for templatedir in available_template_dirs]):
        request.user.message_set.create(message=_('Sorry, that file is not available for editing.'))
        return HttpResponseRedirect(reverse('templatesadmin-overview'))

    if request.method == 'POST':
        formclass = base_form
        for hook in TEMPLATESADMIN_EDITHOOKS:
            formclass.base_fields.update(hook.contribute_to_form(template_path))

        form = formclass(request.POST)
        if form.is_valid():
            content = form.cleaned_data['content']

            try:
                for hook in TEMPLATESADMIN_EDITHOOKS:
                    pre_save_notice = hook.pre_save(request, form, template_path)
                    if pre_save_notice:
                        request.user.message_set.create(message=pre_save_notice)
            except TemplatesAdminException, e:
                request.user.message_set.create(message=e.message)
                return HttpResponseRedirect(request.build_absolute_uri())

            # Save the template
            try:
                f = open(template_path, 'r')
                file_content = f.read()
                f.close()

                # browser tend to strip newlines from <textarea/>s before
                # HTTP-POSTing: re-insert them if neccessary

                # content is in dos-style lineending, will be converted in next step
                if (file_content[-1] == '\n' or file_content[:-2] == '\r\n') \
                   and content[:-2] != '\r\n':
                    content = u"%s\r\n" % content

                # Template is saved in unix-style, save in unix style.
                if None == search("\r\n", file_content):
                    content = content.replace("\r\n", "\n")

                f = codecs.open(template_path, 'w', 'utf-8')
                f.write(content)
                f.close()
            except IOError, e:
                request.user.message_set.create(
                    message=_('Template "%(path)s" has not been saved! Reason: %(errormsg)s' % {
                        'path': path,
                        'errormsg': e
                    })
                )
                return HttpResponseRedirect(request.build_absolute_uri())

            try:
                for hook in TEMPLATESADMIN_EDITHOOKS:
                    post_save_notice = hook.post_save(request, form, template_path)
                    if post_save_notice:
                        request.user.message_set.create(message=post_save_notice)
            except TemplatesAdminException, e:
                request.user.message_set.create(message=e.message)
                return HttpResponseRedirect(request.build_absolute_uri())

            request.user.message_set.create(
                message=_('Template "%s" was saved successfully.' % path)
            )
            return HttpResponseRedirect(reverse('templatesadmin-overview'))
    else:
        template_file = codecs.open(template_path, 'r', 'utf-8').read()

        formclass = TemplateForm
        for hook in TEMPLATESADMIN_EDITHOOKS:
            formclass.base_fields.update(hook.contribute_to_form(template_path))

        form = formclass(
            initial={'content': template_file}
        )

    template_context = {
        'messages': request.user.get_and_delete_messages(),
        'form': form,
        'short_path': path,
        'template_path': path,
        'template_writeable': os.access(template_path, os.W_OK),
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
    }

    return render_to_response(template_name, template_context,
                              RequestContext(request))

# For backwards compatibility and secure out-of-the-box views
overview = user_passes_test(lambda u: user_in_templatesadmin_group(u))(login_required(listing))
edit = user_passes_test(lambda u: user_in_templatesadmin_group(u))(login_required(modify))

import os
import codecs
from datetime import datetime
from stat import ST_MTIME, ST_CTIME
from re import search

from django.conf import settings
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib import messages
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.template.loaders.app_directories import app_template_dirs
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache

from templatesadmin.forms import TemplateForm
from templatesadmin import TemplatesAdminException

# Default settings that may be overriden by global settings (settings.py)
TEMPLATESADMIN_VALID_FILE_EXTENSIONS = getattr(
    settings,
    'TEMPLATESADMIN_VALID_FILE_EXTENSIONS',
    ('html', 'htm', 'txt', 'css', 'backup',)
)

TEMPLATESADMIN_GROUP = getattr(
    settings,
    'TEMPLATESADMIN_GROUP',
    'TemplateAdmins'
)

TEMPLATESADMIN_EDITHOOKS = getattr(
    settings,
    'TEMPLATESADMIN_EDITHOOKS',
    ('templatesadmin.edithooks.dotbackupfiles.DotBackupFilesHook', )
)

TEMPLATESADMIN_HIDE_READONLY = getattr(
    settings,
    'TEMPLATESADMIN_HIDE_READONLY',
    False
)

if str == type(TEMPLATESADMIN_EDITHOOKS):
    TEMPLATESADMIN_EDITHOOKS = (TEMPLATESADMIN_EDITHOOKS,)

_hooks = []

for path in TEMPLATESADMIN_EDITHOOKS:
    # inspired by django.template.context.get_standard_processors
    i = path.rfind('.')
    module, attr = path[:i], path[i+1:]
    try:
        mod = __import__(module, {}, {}, [attr])
    except ImportError, e:
        raise ImproperlyConfigured('Error importing edithook module %s: "%s"' % (module, e))
    try:
        func = getattr(mod, attr)
    except AttributeError:
        raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))

    _hooks.append(func)

TEMPLATESADMIN_EDITHOOKS = tuple(_hooks)

_fixpath = lambda path: os.path.abspath(os.path.normpath(path))

TEMPLATESADMIN_TEMPLATE_DIRS = getattr(
    settings,
    'TEMPLATESADMIN_TEMPLATE_DIRS', [
        d for d in list(settings.TEMPLATE_DIRS) + \
        list(app_template_dirs) if os.path.isdir(d)
    ]
)

TEMPLATESADMIN_TEMPLATE_DIRS = [_fixpath(dir) for dir in TEMPLATESADMIN_TEMPLATE_DIRS]

def user_in_templatesadmin_group(user):
    try:
        user.groups.get(name=TEMPLATESADMIN_GROUP)
        return True
    except ObjectDoesNotExist:
        return False

@never_cache
@user_passes_test(lambda u: user_in_templatesadmin_group(u))
@login_required
def listing(request,
             template_name='templatesadmin/overview.html',
             available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS):

    template_dict = []
    for templatedir in available_template_dirs:
        for root, dirs, files in os.walk(templatedir):
            for f in sorted([f for f in files if f.rsplit('.')[-1] \
                      in TEMPLATESADMIN_VALID_FILE_EXTENSIONS]):
                full_path = os.path.join(root, f)
                l = {
                     'templatedir': templatedir,
                     'rootpath': root,
                     'abspath': full_path,
                     'modified': datetime.fromtimestamp(os.stat(full_path)[ST_MTIME]),
                     'created': datetime.fromtimestamp(os.stat(full_path)[ST_CTIME]),
                     'writeable': os.access(full_path, os.W_OK)
                }

                # Do not fetch non-writeable templates if settings set.
                if (TEMPLATESADMIN_HIDE_READONLY == True and \
                    l['writeable'] == True) or \
                   TEMPLATESADMIN_HIDE_READONLY == False:
                    try:
                        template_dict += (l,)
                    except KeyError:
                        template_dict = (l,)

    template_context = {
        'messages': messages.get_messages(request),
        'template_dict': template_dict,
        'ADMIN_MEDIA_PREFIX': settings.STATIC_URL + '/admin/',
    }

    return render_to_response(template_name, template_context,
                              RequestContext(request))
@never_cache
@user_passes_test(lambda u: user_in_templatesadmin_group(u))
@login_required
def modify(request,
           path,
           template_name='templatesadmin/edit.html',
           base_form=TemplateForm,
           available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS):

    template_path = _fixpath(path)

    # Check if file is within template-dirs
    if not any([template_path.startswith(templatedir) for templatedir in available_template_dirs]):
        request.user.message_set.create(message=_('Sorry, that file is not available for editing.'))
        return HttpResponseRedirect(reverse('templatesadmin-overview'))

    if request.method == 'POST':
        formclass = base_form
        for hook in TEMPLATESADMIN_EDITHOOKS:
            formclass.base_fields.update(hook.contribute_to_form(template_path))

        form = formclass(request.POST)
        if form.is_valid():
            content = form.cleaned_data['content']

            try:
                for hook in TEMPLATESADMIN_EDITHOOKS:
                    pre_save_notice = hook.pre_save(request, form, template_path)
                    if pre_save_notice:
                        messages.add_message(request, messages.INFO, pre_save_notice)
            except TemplatesAdminException, e:
                messages.add_message(request, messages.ERROR, e.message)
            except TemplatesAdminException, e:
                return HttpResponseRedirect(request.build_absolute_uri())

            # Save the template
            try:
                f = open(template_path, 'r')
                file_content = f.read()
                f.close()

                # browser tend to strip newlines from <textarea/>s before
                # HTTP-POSTing: re-insert them if neccessary

                # content is in dos-style lineending, will be converted in next step
                if (file_content[-1] == '\n' or file_content[:-2] == '\r\n') \
                   and content[:-2] != '\r\n':
                    content = u"%s\r\n" % content

                # Template is saved in unix-style, save in unix style.
                if None == search("\r\n", file_content):
                    content = content.replace("\r\n", "\n")

                f = codecs.open(template_path, 'w', 'utf-8')
                f.write(content)
                f.close()
            except IOError, e:
                messages.add_message(request, messages.ERROR, ('Template "%(path)s" has not been saved! Reason: %(errormsg)s' % {
                            'path': path,
                            'errormsg': e
                        })
                )
                return HttpResponseRedirect(request.build_absolute_uri())

            try:
                for hook in TEMPLATESADMIN_EDITHOOKS:
                    post_save_notice = hook.post_save(request, form, template_path)
                    if post_save_notice:
                        messages.add_message(request, messages.INFO, post_save_notice)
            except TemplatesAdminException, e:
                messages.add_message(request, messages.ERROR, e.message)
                return HttpResponseRedirect(request.build_absolute_uri())

            messages.add_message(request, messages.INFO, _('Template "%s" was saved successfully.' % path))
            return HttpResponseRedirect(reverse('templatesadmin-overview'))
    else:
        template_file = codecs.open(template_path, 'r', 'utf-8').read()

        formclass = TemplateForm
        for hook in TEMPLATESADMIN_EDITHOOKS:
            formclass.base_fields.update(hook.contribute_to_form(template_path))

        form = formclass(
            initial={'content': template_file}
        )

    template_context = {
        'messages': messages.get_messages(request),
        'form': form,
        'short_path': path,
        'template_path': path,
        'template_writeable': os.access(template_path, os.W_OK),
        'ADMIN_MEDIA_PREFIX': settings.STATIC_URL + '/admin/',
    }

    return render_to_response(template_name, template_context,
                              RequestContext(request))

# For backwards compatibility and secure out-of-the-box views
overview = user_passes_test(lambda u: user_in_templatesadmin_group(u))(login_required(listing))
edit = user_passes_test(lambda u: user_in_templatesadmin_group(u))(login_required(modify))


Remove Opera sniff

/*!
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery postMessage: Cross-domain scripting goodness
//
// *Version: 0.5, Last updated: 9/11/2009*
//
// Project Home - http://benalman.com/projects/jquery-postmessage-plugin/
// GitHub       - http://github.com/cowboy/jquery-postmessage/
// Source       - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.js
// (Minified)   - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.min.js (0.9kb)
//
// About: License
//
// Copyright (c) 2009 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// This working example, complete with fully commented code, illustrates one
// way in which this plugin can be used.
//
// Iframe resizing - http://benalman.com/code/projects/jquery-postmessage/examples/iframe/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with and what browsers it has been tested in.
//
// jQuery Versions - 1.3.2
// Browsers Tested - Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9.
//
// About: Release History
//
// 0.5 - (9/11/2009) Improved cache-busting
// 0.4 - (8/25/2009) Initial release

(function($){
  '$:nomunge'; // Used by YUI compressor.

  // A few vars used in non-awesome browsers.
  var interval_id,
    last_hash,
    cache_bust = 1,

    // A var used in awesome browsers.
    rm_callback,

    // A few convenient shortcuts.
    window = this,
    FALSE = !1,

    // Reused internal strings.
    postMessage = 'postMessage',
    addEventListener = 'addEventListener',

    p_receiveMessage,

    // I couldn't get window.postMessage to actually work in Opera 9.64!
    has_postMessage = window[postMessage] && !$.browser.opera;

  // Method: jQuery.postMessage
  //
  // This method will call window.postMessage if available, setting the
  // targetOrigin parameter to the base of the target_url parameter for maximum
  // security in browsers that support it. If window.postMessage is not available,
  // the target window's location.hash will be used to pass the message. If an
  // object is passed as the message param, it will be serialized into a string
  // using the jQuery.param method.
  //
  // Usage:
  //
  // > jQuery.postMessage( message, target_url [, target ] );
  //
  // Arguments:
  //
  //  message - (String) A message to be passed to the other frame.
  //  message - (Object) An object to be serialized into a params string, using
  //    the jQuery.param method.
  //  target_url - (String) The URL of the other frame this window is
  //    attempting to communicate with. This must be the exact URL (including
  //    any query string) of the other window for this script to work in
  //    browsers that don't support window.postMessage.
  //  target - (Object) A reference to the other frame this window is
  //    attempting to communicate with. If omitted, defaults to `parent`.
  //
  // Returns:
  //
  //  Nothing.

  $[postMessage] = function( message, target_url, target ) {
    if ( !target_url ) { return; }

    // Serialize the message if not a string. Note that this is the only real
    // jQuery dependency for this script. If removed, this script could be
    // written as very basic JavaScript.
    message = typeof message === 'string' ? message : $.param( message );

    // Default to parent if unspecified.
    target = target || parent;

    if ( has_postMessage ) {
      // The browser supports window.postMessage, so call it with a targetOrigin
      // set appropriately, based on the target_url parameter.
      target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );

    } else if ( target_url ) {
      // The browser does not support window.postMessage, so set the location
      // of the target to target_url#message. A bit ugly, but it works! A cache
      // bust parameter is added to ensure that repeat messages trigger the
      // callback.
      target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (cache_bust++) + '&' + message;
    }
  };

  // Method: jQuery.receiveMessage
  //
  // Register a single callback for either a window.postMessage call, if
  // supported, or if unsupported, for any change in the current window
  // location.hash. If window.postMessage is supported and source_origin is
  // specified, the source window will be checked against this for maximum
  // security. If window.postMessage is unsupported, a polling loop will be
  // started to watch for changes to the location.hash.
  //
  // Note that for simplicity's sake, only a single callback can be registered
  // at one time. Passing no params will unbind this event (or stop the polling
  // loop), and calling this method a second time with another callback will
  // unbind the event (or stop the polling loop) first, before binding the new
  // callback.
  //
  // Also note that if window.postMessage is available, the optional
  // source_origin param will be used to test the event.origin property. From
  // the MDC window.postMessage docs: This string is the concatenation of the
  // protocol and "://", the host name if one exists, and ":" followed by a port
  // number if a port is present and differs from the default port for the given
  // protocol. Examples of typical origins are https://example.org (implying
  // port 443), http://example.net (implying port 80), and http://example.com:8080.
  //
  // Usage:
  //
  // > jQuery.receiveMessage( callback [, source_origin ] [, delay ] );
  //
  // Arguments:
  //
  //  callback - (Function) This callback will execute whenever a <jQuery.postMessage>
  //    message is received, provided the source_origin matches. If callback is
  //    omitted, any existing receiveMessage event bind or polling loop will be
  //    canceled.
  //  source_origin - (String) If window.postMessage is available and this value
  //    is not equal to the event.origin property, the callback will not be
  //    called.
  //  source_origin - (Function) If window.postMessage is available and this
  //    function returns false when passed the event.origin property, the
  //    callback will not be called.
  //  delay - (Number) An optional zero-or-greater delay in milliseconds at
  //    which the polling loop will execute (for browser that don't support
  //    window.postMessage). If omitted, defaults to 100.
  //
  // Returns:
  //
  //  Nothing!

  $.receiveMessage = p_receiveMessage = function( callback, source_origin, delay ) {
    if ( has_postMessage ) {
      // Since the browser supports window.postMessage, the callback will be
      // bound to the actual event associated with window.postMessage.

      if ( callback ) {
        // Unbind an existing callback if it exists.
        rm_callback && p_receiveMessage();

        // Bind the callback. A reference to the callback is stored for ease of
        // unbinding.
        rm_callback = function(e) {
          if ( ( typeof source_origin === 'string' && e.origin !== source_origin )
            || ( $.isFunction( source_origin ) && source_origin( e.origin ) === FALSE ) ) {
            return FALSE;
          }
          callback( e );
        };
      }

      if ( window[addEventListener] ) {
        window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
      } else {
        window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
      }

    } else {
      // Since the browser sucks, a polling loop will be started, and the
      // callback will be called whenever the location.hash changes.

      interval_id && clearInterval( interval_id );
      interval_id = null;

      if ( callback ) {
        delay = typeof source_origin === 'number'
          ? source_origin
          : typeof delay === 'number'
            ? delay
            : 100;

        interval_id = setInterval(function(){
          var hash = document.location.hash,
            re = /^#?\d+&/;
          if ( hash !== last_hash && re.test( hash ) ) {
            last_hash = hash;
            callback({ data: hash.replace( re, '' ) });
          }
        }, delay );
      }
    }
  };

})(jQuery);

/*
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var g,d,j=1,a,b=this,f=!1,h="postMessage",e="addEventListener",c,i=b[h]&&!$.browser.opera;$[h]=function(k,l,m){if(!l){return}k=typeof k==="string"?k:$.param(k);m=m||parent;if(i){m[h](k,l.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))}else{if(l){m.location=l.replace(/#.*$/,"")+"#"+(+new Date)+(j++)+"&"+k}}};$.receiveMessage=c=function(l,m,k){if(i){if(l){a&&c();a=function(n){if((typeof m==="string"&&n.origin!==m)||($.isFunction(m)&&m(n.origin)===f)){return f}l(n)}}if(b[e]){b[l?e:"removeEventListener"]("message",a,f)}else{b[l?"attachEvent":"detachEvent"]("onmessage",a)}}else{g&&clearInterval(g);g=null;if(l){k=typeof m==="number"?m:typeof k==="number"?k:100;g=setInterval(function(){var o=document.location.hash,n=/^#?\d+&/;if(o!==d&&n.test(o)){d=o;l({data:o.replace(n,"")})}},k)}}}})(jQuery);

/*!
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery postMessage: Cross-domain scripting goodness
//
// *Version: 0.5, Last updated: 9/11/2009*
//
// Project Home - http://benalman.com/projects/jquery-postmessage-plugin/
// GitHub       - http://github.com/cowboy/jquery-postmessage/
// Source       - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.js
// (Minified)   - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.min.js (0.9kb)
//
// About: License
//
// Copyright (c) 2009 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// This working example, complete with fully commented code, illustrates one
// way in which this plugin can be used.
//
// Iframe resizing - http://benalman.com/code/projects/jquery-postmessage/examples/iframe/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with and what browsers it has been tested in.
//
// jQuery Versions - 1.3.2
// Browsers Tested - Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9.
//
// About: Release History
//
// 0.5 - (9/11/2009) Improved cache-busting
// 0.4 - (8/25/2009) Initial release

(function($){
  '$:nomunge'; // Used by YUI compressor.

  // A few vars used in non-awesome browsers.
  var interval_id,
    last_hash,
    cache_bust = 1,

    // A var used in awesome browsers.
    rm_callback,

    // A few convenient shortcuts.
    window = this,
    FALSE = !1,

    // Reused internal strings.
    postMessage = 'postMessage',
    addEventListener = 'addEventListener',

    p_receiveMessage,

    has_postMessage = window[postMessage];

  // Method: jQuery.postMessage
  //
  // This method will call window.postMessage if available, setting the
  // targetOrigin parameter to the base of the target_url parameter for maximum
  // security in browsers that support it. If window.postMessage is not available,
  // the target window's location.hash will be used to pass the message. If an
  // object is passed as the message param, it will be serialized into a string
  // using the jQuery.param method.
  //
  // Usage:
  //
  // > jQuery.postMessage( message, target_url [, target ] );
  //
  // Arguments:
  //
  //  message - (String) A message to be passed to the other frame.
  //  message - (Object) An object to be serialized into a params string, using
  //    the jQuery.param method.
  //  target_url - (String) The URL of the other frame this window is
  //    attempting to communicate with. This must be the exact URL (including
  //    any query string) of the other window for this script to work in
  //    browsers that don't support window.postMessage.
  //  target - (Object) A reference to the other frame this window is
  //    attempting to communicate with. If omitted, defaults to `parent`.
  //
  // Returns:
  //
  //  Nothing.

  $[postMessage] = function( message, target_url, target ) {
    if ( !target_url ) { return; }

    // Serialize the message if not a string. Note that this is the only real
    // jQuery dependency for this script. If removed, this script could be
    // written as very basic JavaScript.
    message = typeof message === 'string' ? message : $.param( message );

    // Default to parent if unspecified.
    target = target || parent;

    if ( has_postMessage ) {
      // The browser supports window.postMessage, so call it with a targetOrigin
      // set appropriately, based on the target_url parameter.
      target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );

    } else if ( target_url ) {
      // The browser does not support window.postMessage, so set the location
      // of the target to target_url#message. A bit ugly, but it works! A cache
      // bust parameter is added to ensure that repeat messages trigger the
      // callback.
      target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (cache_bust++) + '&' + message;
    }
  };

  // Method: jQuery.receiveMessage
  //
  // Register a single callback for either a window.postMessage call, if
  // supported, or if unsupported, for any change in the current window
  // location.hash. If window.postMessage is supported and source_origin is
  // specified, the source window will be checked against this for maximum
  // security. If window.postMessage is unsupported, a polling loop will be
  // started to watch for changes to the location.hash.
  //
  // Note that for simplicity's sake, only a single callback can be registered
  // at one time. Passing no params will unbind this event (or stop the polling
  // loop), and calling this method a second time with another callback will
  // unbind the event (or stop the polling loop) first, before binding the new
  // callback.
  //
  // Also note that if window.postMessage is available, the optional
  // source_origin param will be used to test the event.origin property. From
  // the MDC window.postMessage docs: This string is the concatenation of the
  // protocol and "://", the host name if one exists, and ":" followed by a port
  // number if a port is present and differs from the default port for the given
  // protocol. Examples of typical origins are https://example.org (implying
  // port 443), http://example.net (implying port 80), and http://example.com:8080.
  //
  // Usage:
  //
  // > jQuery.receiveMessage( callback [, source_origin ] [, delay ] );
  //
  // Arguments:
  //
  //  callback - (Function) This callback will execute whenever a <jQuery.postMessage>
  //    message is received, provided the source_origin matches. If callback is
  //    omitted, any existing receiveMessage event bind or polling loop will be
  //    canceled.
  //  source_origin - (String) If window.postMessage is available and this value
  //    is not equal to the event.origin property, the callback will not be
  //    called.
  //  source_origin - (Function) If window.postMessage is available and this
  //    function returns false when passed the event.origin property, the
  //    callback will not be called.
  //  delay - (Number) An optional zero-or-greater delay in milliseconds at
  //    which the polling loop will execute (for browser that don't support
  //    window.postMessage). If omitted, defaults to 100.
  //
  // Returns:
  //
  //  Nothing!

  $.receiveMessage = p_receiveMessage = function( callback, source_origin, delay ) {
    if ( has_postMessage ) {
      // Since the browser supports window.postMessage, the callback will be
      // bound to the actual event associated with window.postMessage.

      if ( callback ) {
        // Unbind an existing callback if it exists.
        rm_callback && p_receiveMessage();

        // Bind the callback. A reference to the callback is stored for ease of
        // unbinding.
        rm_callback = function(e) {
          if ( ( typeof source_origin === 'string' && e.origin !== source_origin )
            || ( $.isFunction( source_origin ) && source_origin( e.origin ) === FALSE ) ) {
            return FALSE;
          }
          callback( e );
        };
      }

      if ( window[addEventListener] ) {
        window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
      } else {
        window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
      }

    } else {
      // Since the browser sucks, a polling loop will be started, and the
      // callback will be called whenever the location.hash changes.

      interval_id && clearInterval( interval_id );
      interval_id = null;

      if ( callback ) {
        delay = typeof source_origin === 'number'
          ? source_origin
          : typeof delay === 'number'
            ? delay
            : 100;

        interval_id = setInterval(function(){
          var hash = document.location.hash,
            re = /^#?\d+&/;
          if ( hash !== last_hash && re.test( hash ) ) {
            last_hash = hash;
            callback({ data: hash.replace( re, '' ) });
          }
        }, delay );
      }
    }
  };

})(jQuery);

/*
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var g,d,j=1,a,b=this,f=!1,h="postMessage",e="addEventListener",c,i=b[h];$[h]=function(k,l,m){if(!l){return}k=typeof k==="string"?k:$.param(k);m=m||parent;if(i){m[h](k,l.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))}else{if(l){m.location=l.replace(/#.*$/,"")+"#"+(+new Date)+(j++)+"&"+k}}};$.receiveMessage=c=function(l,m,k){if(i){if(l){a&&c();a=function(n){if((typeof m==="string"&&n.origin!==m)||($.isFunction(m)&&m(n.origin)===f)){return f}l(n)}}if(b[e]){b[l?e:"removeEventListener"]("message",a,f)}else{b[l?"attachEvent":"detachEvent"]("onmessage",a)}}else{g&&clearInterval(g);g=null;if(l){k=typeof m==="number"?m:typeof k==="number"?k:100;g=setInterval(function(){var o=document.location.hash,n=/^#?\d+&/;if(o!==d&&n.test(o)){d=o;l({data:o.replace(n,"")})}},k)}}}})(jQuery);


adding TUN type

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>

#include <netinet/ip.h>

#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>

#include "help.h"

#define CLEAR(x) \
    memset((x), 0, sizeof(*(x)))

struct tap_desc
{
    int32_t      desc;
    int32_t      sock;
    struct ifreq ifr;
};

tap_desc_t * init_tap()
{
    return (tap_desc_t *) calloc(1,sizeof(tap_desc_t));
}

void finish_tap(tap_desc_t * td)
{
    free(td);
}

int32_t open_tap(tap_desc_t * td, char * name)
{
    char * dev = "/dev/net/tun";

    if (0 == td)
    {
        return -100;
    }
    else if (0 == name)
    {
        return -101;
    }

    int32_t fd = open(dev, O_RDWR);

    if (fd < 0)
    {
        fprintf(stderr,"open(%s): %s\n", name, strerror(errno));
        return -1;
    }
    else
    {
        td->desc = fd;
    }

    CLEAR(&(td->ifr));

    td->sock = socket(AF_INET, SOCK_DGRAM, 0);
    td->ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

    if (*name)
        strncpy(td->ifr.ifr_name, name, IFNAMSIZ);

    if (ioctl(td->desc, TUNSETIFF, (void *)&td->ifr) < 0)
    {
        fprintf(stderr,"TUNSETIFF: %s\n", strerror(errno));
        return -2;
    }

    return 0;
}

int32_t close_tap(tap_desc_t * td)
{
    if (0 <= td->desc)
    {
        close(td->desc);
    }
    if (0 <= td->sock)
    {
        close(td->sock);
    }

    return 0;
}

int32_t bring_up_tap(tap_desc_t * td)
{
    td->ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);

    if (ioctl(td->sock, SIOCSIFFLAGS, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFFLAGS: %s\n", strerror(errno));
        return -3;
    }

    return 0;
}

int32_t set_mtu(tap_desc_t * td, uint32_t mtu)
{
    td->ifr.ifr_mtu = mtu;

    if (ioctl(td->sock, SIOCSIFMTU, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFMTU: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t set_ip(tap_desc_t * td, uint32_t ip)
{
    struct sockaddr_in addr;
    CLEAR(&addr);

    addr.sin_addr.s_addr = ip;
    addr.sin_family = AF_INET;
    memcpy(&td->ifr.ifr_addr, &addr, sizeof(addr));

    if (ioctl(td->sock, SIOCSIFADDR, &td->ifr) < 0)
    {
        fprintf(stderr, "SIOCSIFADDR: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t set_mask(tap_desc_t * td, uint32_t mask)
{
    struct sockaddr_in addr;
    CLEAR(&addr);

    addr.sin_addr.s_addr = mask;
    addr.sin_family = AF_INET;
    memcpy(&td->ifr.ifr_addr, &addr, sizeof(addr));

    if ( ioctl(td->sock, SIOCSIFNETMASK, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFNETMASK: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t get_mac(tap_desc_t * td, MACAddr mac)
{
    if (ioctl(td->sock, SIOCGIFHWADDR, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCGIFHWADDR: %s\n", strerror(errno));
        return -1;
    }
    else
    {
        memcpy(mac,&td->ifr.ifr_hwaddr.sa_data, sizeof(MACAddr));
    }

    return 0;
}

int32_t read_tap(tap_desc_t * td, uint8_t * buf, int32_t len)
{
    struct pollfd p = { td->desc, POLLIN, 0 };

    int32_t ret = poll(&p, 1, 500);

    if (ret == 0)
    {
        return 0;
    }
    else if (ret < 0)
    {
        fprintf(stderr, "ERROR: poll returned %d!!!\n", ret);
        return ret;
    }
    else if (p.revents | POLLIN)
    {
        return read(td->desc,buf,len);
    }
    else
    {
        fprintf(stderr, "ERROR: poll returned, it wasn't a timout, and there "
                        "isn't any data to be read!\n");
        return -100;
    }
}

int32_t write_tap(tap_desc_t * td, const uint8_t * buf, int32_t len)
{
    return write(td->desc,buf,len);
}

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>

#include <netinet/ip.h>

#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>

#include "help.h"

#define CLEAR(x) \
    memset((x), 0, sizeof(*(x)))

struct tap_desc
{
    int32_t      desc;
    int32_t      sock;
    struct ifreq ifr;
};

tap_desc_t * init_tap()
{
    return (tap_desc_t *) calloc(1,sizeof(tap_desc_t));
}

void finish_tap(tap_desc_t * td)
{
    free(td);
}

int32_t open_tap(tap_desc_t * td, char * name, int type)
{
    char * dev = "/dev/net/tun";

    if (0 == td)
    {
        return -100;
    }
    else if (0 == name)
    {
        return -101;
    }

    int32_t fd = open(dev, O_RDWR);

    if (fd < 0)
    {
        fprintf(stderr,"open(%s): %s\n", name, strerror(errno));
        return -1;
    }
    else
    {
        td->desc = fd;
    }

    CLEAR(&(td->ifr));

    td->sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (type == 0)
        td->ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
    else
        td->ifr.ifr_flags = IFF_TUN | IFF_NO_PI;

    if (*name)
        strncpy(td->ifr.ifr_name, name, IFNAMSIZ);

    if (ioctl(td->desc, TUNSETIFF, (void *)&td->ifr) < 0)
    {
        fprintf(stderr,"TUNSETIFF: %s\n", strerror(errno));
        return -2;
    }

    return 0;
}

int32_t close_tap(tap_desc_t * td)
{
    if (0 <= td->desc)
    {
        close(td->desc);
    }
    if (0 <= td->sock)
    {
        close(td->sock);
    }

    return 0;
}

int32_t bring_up_tap(tap_desc_t * td)
{
    td->ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);

    if (ioctl(td->sock, SIOCSIFFLAGS, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFFLAGS: %s\n", strerror(errno));
        return -3;
    }

    return 0;
}

int32_t set_mtu(tap_desc_t * td, uint32_t mtu)
{
    td->ifr.ifr_mtu = mtu;

    if (ioctl(td->sock, SIOCSIFMTU, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFMTU: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t set_ip(tap_desc_t * td, uint32_t ip)
{
    struct sockaddr_in addr;
    CLEAR(&addr);

    addr.sin_addr.s_addr = ip;
    addr.sin_family = AF_INET;
    memcpy(&td->ifr.ifr_addr, &addr, sizeof(addr));

    if (ioctl(td->sock, SIOCSIFADDR, &td->ifr) < 0)
    {
        fprintf(stderr, "SIOCSIFADDR: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t set_mask(tap_desc_t * td, uint32_t mask)
{
    struct sockaddr_in addr;
    CLEAR(&addr);

    addr.sin_addr.s_addr = mask;
    addr.sin_family = AF_INET;
    memcpy(&td->ifr.ifr_addr, &addr, sizeof(addr));

    if ( ioctl(td->sock, SIOCSIFNETMASK, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCSIFNETMASK: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int32_t get_mac(tap_desc_t * td, MACAddr mac)
{
    if (ioctl(td->sock, SIOCGIFHWADDR, &td->ifr) < 0)
    {
        fprintf(stderr,"SIOCGIFHWADDR: %s\n", strerror(errno));
        return -1;
    }
    else
    {
        memcpy(mac,&td->ifr.ifr_hwaddr.sa_data, sizeof(MACAddr));
    }

    return 0;
}

int32_t read_tap(tap_desc_t * td, uint8_t * buf, int32_t len)
{
    struct pollfd p = { td->desc, POLLIN, 0 };

    int32_t ret = poll(&p, 1, 500);

    if (ret == 0)
    {
        return 0;
    }
    else if (ret < 0)
    {
        fprintf(stderr, "ERROR: poll returned %d!!!\n", ret);
        return ret;
    }
    else if (p.revents | POLLIN)
    {
        return read(td->desc,buf,len);
    }
    else
    {
        fprintf(stderr, "ERROR: poll returned, it wasn't a timout, and there "
                        "isn't any data to be read!\n");
        return -100;
    }
}

int32_t write_tap(tap_desc_t * td, const uint8_t * buf, int32_t len)
{
    return write(td->desc,buf,len);
}