Wolfmans Howlings

A programmers Blog about Ruby, Rails and a few other issues

Updated Capistrano local subversion and perforce

Posted by Jim Morris Sun, 18 Feb 2007 23:32:00 GMT

I have updated the Capistrano local subversion module and added a perforce one.

The original article is here.

I have added rsync as an option to speed up the deployment to remote servers, To use that option install this version, in the example below I have installed the file into my rails projects lib/tasks directory.

When the rsync option is set the deployment method will use rsync over ssh to synchronize between a local cache of the subversion project and a remote cache, minimizing the amount of data uploaded to the server.

To use rsync use this in the deploy.rb...

  require 'lib/tasks/local_subversion_rsync.rb'
  set :scm, Capistrano::SCM::LocalSubversionRsync

  set :repository_is_not_reachable_from_remote, true
  set :use_rsync, true
  set :local_rsync_cache, "/home/user/projects/aproject/cache"
  set :remote_rsync_cache, "/var/www/webapp/cache"

  set :rsync_username, "ausername"
  set :rsync_excludes, ["*.bak", "*.log"]

The rsync_username and rsync_excludes are optional.

The :rsync_username option sets the username that ssh uses to login to use rsync. Leave it blank if it is the same as your current login name.

The :rsync_excludes option allows you to pass --exclude options to rsync, using the rsync syntax for exclusions, this is an array of patterns to exclude.

Note you must do a local SVN checkout to the directory specified by local_rsync_cache at least once before using this method.

Also the directory specified by remote_rsync_cache must exist on the server.

The perforce version of the scm module that allows the perforce server to be accessed locally only is here, note that this does not currently support the rsync option or write to the revisions.log.

Read more...

Posted in  | Tags  | 1 comment | no trackbacks

a Capistrano scm module for local SVN access

Posted by Jim Morris Thu, 07 Dec 2006 05:42:00 GMT

UPDATE 2007-06-09 This method has been deprecated in Cap 2.0.

UPDATE 2007-02-21 I have updated the files to correctly update revisions.log

UPDATE I added rsync as suggested see end of article for more info.

I previously published a custom deploy recipe that allowed subversion to checkout from a local repository and send to the remote server.

This was a suboptimal solution, as it wouldn't work with things like deprec.

So here is the way I should have done it in the first place. It is a modified version of the standard subversion scm module, it should be backward compatible with the built in version, but adds a couple of features.

  • Handles the subversion repository only being accessible from the local machine
  • If the subversion repository is accessible from the remote server allows for different URLs for access from the local and remote machines.

If you use a standard deploy.rb and your subversion is accessible from the server, then it should work exactly as the built-in version (no need to use it then, but it should work)

However if you have your subversion server behind your local firewall, you just add these three lines to your deploy.rb

require 'lib/tasks/local_subversion_rsync.rb'
set :scm, Capistrano::SCM::LocalSubversionRsync
set :repository_is_not_reachable_from_remote, true

And everything will work as before, even though the server has no idea what subversion is.

Of course for this to work you need to download this de-tar it and put it in your lib/tasks directory. (That's where the require line gets this extension from). There is also a unit test for the new_subversion module which is extended from the one shipped with Capistrano, which passes.

Everything else in deploy.rb should be the same as before, except that you set the repository to the URL your local machine uses to access your locally accessible subversion server or repository. (Should even work with the URL file://...), for instance...

set :repository, "svn://your.svnserver.host/#{application}/trunk"

The assumption is that both the local and remote machines can create files and directories in /tmp, if this is not true then one or both of these should be set...

set :tmpdir_local, "/usr/tmp"
set :tmpdir_remote, "/home/user/tmp"

The way this module works is to export the relevant version of the project from Subversion into a temporary directory on the local machine (default is /tmp/unique_name). Then I create a gzip'd tar archive into the temp directory. The tar file is then transferred to the target servers, using the Capistrano put command into a temporary directory (/tmp by default) on the server. I then create the target directory on the server and un-tar the file into that directory. Then clean up the various temporary files. From the perspective of the server the end result should be identical to doing an svn export into the target directory on the server.

The other facility this SCM Module provides is when your subversion server is accessible from the server as is the "normal" use case (per the Capistrano developers, but bad practice IMHO), but you need a different URL to access the SVN repository from the local machine vs the remote server, eg

server accesses with this URL svn://localhost/myapp/trunk local machine accesses with this URL svn+ssh://myserver.com/myapp/trunk

in this case you add these lines...

require 'lib/tasks/local_subversion_rsync.rb'
set :scm, Capistrano::SCM::LocalSubversionRsync

set :local_repository_path, "svn+ssh://myserver.com/myapp/trunk"
set :repository, "svn://localhost/myapp/trunk"

In addition if the actual SVN binary is different on server and local you can add these for instance...

set :remote_svn, "/usr/local/bin/svn"
set :local_svn, "/usr/bin/svn"

If the relevant configuration variable is not set then the standard svn configuration variable is used, and then just "svn" if that is not set.

Please beware I have not fully tested the use cases other than the first one, where I use my local subversion server to deploy to my remote servers that know nothing about subversion, however the unit tests do pass for the other use cases.

UPDATE 2007-02-15 check this article for using the rsync option.

Posted in ,  | Tags ,  | 26 comments | no trackbacks

A HAML edit mode for JEdit

Posted by Jim Morris Sat, 02 Dec 2006 02:09:00 GMT

I've just started a new RoR project and thought I'd try out HAML for the views instead of rhtml.

Seeing as I just switched to JEdit, I looked for a HAML language mode, and to my surprise no one had done one yet.

So here is my first attempt at a language mode for JEdit and version 1.0 of the HAML language mode. It needs more work, and I hope someone with a better understanding of JEdit language modes will help out.

This will set the indent to 2 spaces and auto-indent after a %tag that has nothing else on the line...

%p
  stuff

It will not auto indent if there is more stuff on the line like in the following example the line after %p gets indented but the line after %label does not.

 %p
    %label{ :for => "login" }Login
    %br/
    = f.text_field :login

I have setup a basic syntax coloring scheme that should color %tags and .classes and #ids differently. It will color anything following an = or ~ and defer the syntax highlighting to the ruby mode for anything following them, ditto for anything inside { }.

Feedback and better ideas are welcome.

Download from here

To install copy the file to ~/.jedit/modes, and edit the catalog file in that directory and add this line:

<MODE NAME="haml" FILE="haml.xml" FILE_NAME_GLOB="*.haml" />

Then restart jedit, and your haml files will be syntax highlighted.

I like HAML but there simply is not enough example code out there yet, so here are two simple conversions from well know rhtml files into HAML...

First the standard login form from acts_as_authenticated

- form_tag sessions_path do
  %p
    %label{ :for => "login"} Login
    %br/
    = text_field_tag 'login'

  %p
    %label{ :for => "password" }Password
    %br/
    = password_field_tag 'password'

  %p
    %label{ :for => "remember_me" }Remember me:
    = check_box_tag 'remember_me'

  %p
    = submit_tag 'Log in'

Next the new user form from the same place...

= error_messages_for :user

- form_for :user, :url => users_path do |f|
  %p
    %label{ :for => "login" }Login
    %br/
    = f.text_field :login
  %p
    %label{ :for => "email" }Email
    %br/
    = f.text_field :email

  %p
    %label{ :for => "password" }Password
    %br/
    = f.password_field :password

  %p
    %label{ :for => "password_confirmation" } Confirm Password
    %br/
    = f.password_field :password_confirmation

  %p
    = submit_tag 'Sign up'

If this is dumb HAML coding please let me know how to improve it :)

Posted in , ,  | Tags , ,  | 6 comments | no trackbacks

JEdit Ruby/Rails Snippets or superabbrevs

Posted by Jim Morris Sat, 25 Nov 2006 03:05:00 GMT

I imported the rest of the Textmate ruby and rails snippets I had to combine them into the one ruby file though.

I have added about 80 new ones to the existing ones by Scott Becker that I found here

The list of snippets I now have is at the end of this post, note I did change the abbreviation for some of the assertions they all now start with as so I can search for them faster with my select_superabbrevs macro described in the previous post.

download the new snippets from here unzip and replace the ruby file in the .jedit/SuperAbbrevs directory with the one in this zip archive.

You need the Beta version of SuperAbbrevs to use this from here

Super Abbreviations for the ruby mode

--------------------------------------- : --------------------------------------
:${1:key} => ${2:"value"}$end

-------------------------------------- all -------------------------------------
all? { |${1:e}| $end }

-------------------------------------- am --------------------------------------
alias_method :${1:new_name}, :${2:old_name}

-------------------------------------- any -------------------------------------
any? { |${1:e}| $end }

-------------------------------------- app -------------------------------------
if __FILE__ == \$PROGRAM_NAME
    $end
end

-------------------------------------- ase -------------------------------------
assert_equal ${1:value}, @${2:thing}.${3:attr}$end

------------------------------------- asid -------------------------------------
assert_in_delta(${1:expected_float}, ${2:actual_float}, ${3:2 ** -20})

------------------------------------- asio -------------------------------------
assert_instance_of(${1:ExpectedClass}, ${2:actual_instance})$end

------------------------------------- asko -------------------------------------
assert_kind_of ${1:Class}, @${2:thing}$end

-------------------------------------- asm -------------------------------------
assert_match(/${1:expected_pattern}/, ${2:actual_string})

-------------------------------------- asn -------------------------------------
assert_nil(${1:instance})$end

------------------------------------- asne -------------------------------------
assert_not_equal(${1:unexpected}, ${2:actual})

------------------------------------- asnm -------------------------------------
assert_no_match(/${1:unexpected_pattern}/, ${2:actual_string})

------------------------------------- asnn -------------------------------------
assert_not_nil ${1:true}$end

------------------------------------- asnr -------------------------------------
assert_nothing_raised(${1:Exception}) { $end }

------------------------------------- asns -------------------------------------
assert_not_same(${1:unexpected}, ${2:actual})$end

------------------------------------- asnt -------------------------------------
assert_nothing_thrown { $end }

-------------------------------------- aso -------------------------------------
assert_operator(${1:left}, :${2:operator}, ${3:right})

-------------------------------------- asr -------------------------------------
assert_raise(${1:ActiveRecord::RecordNotFound}) { ${2:Class}.find(@${3:thing}.${4:id}) }$end

------------------------------------- asrar ------------------------------------
assert_raise(${1:ActiveRecord::RecordNotFound}) { ${2:Class}.find(@${3:thing}.${4:id}) }

------------------------------------- asre -------------------------------------
assert_response :${1:success}$end

------------------------------------- asrt -------------------------------------
assert_redirected_to :action => "${1:index}"$end

-------------------------------------- ass -------------------------------------
assert(${1:test}, "${2:Failure message.}")

------------------------------------- assm -------------------------------------
assert_same(${1:expected}, ${2:actual})$end

-------------------------------------- ast -------------------------------------
assert_throws(:${1:expected}) { $end }

-------------------------------------- bt --------------------------------------
belongs_to :${1:object}, :class_name => "${2:ClassName}", :foreign_key => "${3:foreign_key}_id"$end

------------------------------------- case -------------------------------------
case ${1:object}
when ${2:condition}
    $end
end

-------------------------------------- cl --------------------------------------
classify { |${1:e}| $end }

------------------------------------- clafn ------------------------------------
split("::").inject(Object) { |par, const| par.const_get(const) }

------------------------------------- class ------------------------------------
class ${1:ClassName}
    $end
end

-------------------------------------- col -------------------------------------
collect { |${1:e}| $end }

------------------------------------ collect -----------------------------------
collect { |${1:element}| ${1:element}.$2 }$end

------------------------------------- Comp -------------------------------------
include Comparable

def <=>(other)
    $end
end

-------------------------------------- dee -------------------------------------
Marshal.load(Marshal.dump(${1:obj_to_copy}))

-------------------------------------- def -------------------------------------
def ${1:method_name}
  $end
end

------------------------------------- defd -------------------------------------
def_delegator :${1:@del_obj}, :${2:del_meth}, :${3:new_name}

------------------------------------- defds ------------------------------------
def_delegators :${1:@del_obj}, :${2:del_methods}

------------------------------------- defs -------------------------------------
def self.${1:class_method_name}
    $end
end

------------------------------------- deft -------------------------------------
def test_${1:case_name}
    $end
end

-------------------------------------- det -------------------------------------
detect { |${1:e}| $end }

-------------------------------------- do --------------------------------------
do
    $end
end

-------------------------------------- doo -------------------------------------
do |${1:object}|
    $end
end

-------------------------------------- ea --------------------------------------
each { |${1:e}| $end }

-------------------------------------- eab -------------------------------------
each_byte { |${1:byte}| $end }

-------------------------------------- eac -------------------------------------
each_char { |${1:chr}| $end }

------------------------------------- each -------------------------------------
each { |${1:element}| ${1:element}.$end }

-------------------------------- each_with_index -------------------------------
each_with_index { |${1:element}, ${2:idx}| ${1:element}.$end }

-------------------------------------- eai -------------------------------------
each_index { |${1:i}| $end }

-------------------------------------- eak -------------------------------------
each_key { |${1:key}| $end }

-------------------------------------- eal -------------------------------------
each_line$1 { |${2:line}| $end }

-------------------------------------- eap -------------------------------------
each_pair { |${1:name}, ${2:val}| $end }

------------------------------------- easl -------------------------------------
each_slice(${1:2}) { |${2:group}| $end }

-------------------------------------- eav -------------------------------------
each_value { |${1:val}| $end }

------------------------------------- eawi -------------------------------------
each_with_index { |${1:e}, ${2:i}| $end }

------------------------------------- Enum -------------------------------------
include Enumerable

def each(&block)
    $end
end

-------------------------------------- fin -------------------------------------
find { |${1:e}| $end }

------------------------------------- fina -------------------------------------
find_all { |${1:e}| $end }

-------------------------------------- fl --------------------------------------
flunk("${1:Failure message.}")

------------------------------------- flao -------------------------------------
inject(Array.new) { |${1:arr}, ${2:a}| ${1:arr}.push(*${2:a}) }

------------------------------------- flash ------------------------------------
flash[:${1:notice}] = "${2:Successfully created...}"$end

------------------------------------- forin ------------------------------------
for ${1:element} in ${2:collection}
    ${1:element}.$3
end$end

------------------------------------- Forw -------------------------------------
extend Forwardable

-------------------------------------- gre -------------------------------------
grep(${1:/${2:pattern}/}) { |${3:match}| $end }

------------------------------------- Hash -------------------------------------
Hash.new { |${1:hash}, ${2:key}| ${1:hash}[${2:key}] = $end }

-------------------------------------- hm --------------------------------------
has_many :${1:objects}, :class_name => "${2:ClassName}", :foreign_key => "${3:foreign_key}_id"$end

-------------------------------------- ho --------------------------------------
has_one :${1:object}, :class_name => "${2:ClassName}", :foreign_key => "${3:foreign_key}_id"$end

-------------------------------------- if --------------------------------------
if ${1:condition}
    $end
end

-------------------------------------- ife -------------------------------------
if ${1:condition}
    $2
else
    $3
end

-------------------------------------- inj -------------------------------------
inject(${1:init}) { |${2:mem}, ${3:var}| $end }

------------------------------------ inject ------------------------------------
inject(${1:object}) { |${2:injection}, ${3:element}| $4 }$end

------------------------------------- logi -------------------------------------
logger.info "${1:Current value is...}"$end

-------------------------------------- mac -------------------------------------
add_column :${1:table_name}, :${2:column_name}, :${3:string}$end

-------------------------------------- mai -------------------------------------
add_index :${1:table_name},[:${2:column_name}], :name => "${2:column_name}_index"$end

-------------------------------------- map -------------------------------------
map { |${1:e}| $end }

------------------------------------- mapwi ------------------------------------
enum_with_index.map { |${1:e}, ${2:i}| $end }

-------------------------------------- max -------------------------------------
max { |a, b| $end }

-------------------------------------- mcc -------------------------------------
change_column :${1:table_name}, :${2:column_name}, :${3:string}, ${4:default => 1}$end

-------------------------------------- mct -------------------------------------
create_table :${1:table_name} do |t|
  $2
end

-------------------------------------- Md --------------------------------------
File.open(${1:"${2:path/to/file}.dump"}, "w") { |${3:file}| Marshal.dump(${4:obj}, ${3:file}) }

-------------------------------------- mdt -------------------------------------
drop_table :${1:table_name}$end

-------------------------------------- mex -------------------------------------
execute "$1"$end

-------------------------------------- min -------------------------------------
min { |a, b| $end }

-------------------------------------- mm --------------------------------------
def method_missing(meth, *args, &block)
    $end
end

-------------------------------------- mnc -------------------------------------
rename_column :${1:table_name}, :${2:column_name}, :${3:new_column}$end

-------------------------------------- mod -------------------------------------
module ${1:ModuleName}
    module_function

    $end
end

-------------------------------------- mrc -------------------------------------
remove_column :${1:table_name}, :${2:column_name}$end

-------------------------------------- mri -------------------------------------
remove_index :${1:table_name}, :${2:column_name}$end

-------------------------------------- mtc -------------------------------------
t.column :${1:column_name}, :${2:string}$end

--------------------------------------- p --------------------------------------
params[:${1:id}]$end

-------------------------------------- par -------------------------------------
partition { |${1:e}| $end }

--------------------------------------- r --------------------------------------
attr_reader :${1:attr_names}

-------------------------------------- ra --------------------------------------
render :action => "${1:action}"$end

-------------------------------------- ral -------------------------------------
render :action => "${1:action}", :layout => "${2:layoutname}"$end

-------------------------------------- ran -------------------------------------
sort_by { rand }

-------------------------------------- rb --------------------------------------
#!/usr/bin/env ruby -w



------------------------------------- rcea -------------------------------------
render_component :action => "${1:index}"$end

------------------------------------- rcec -------------------------------------
render_component :controller => "${1:items}"$end

------------------------------------- rceca ------------------------------------
render_component :controller => "${1:items}", :action => "${2:index}"$end

-------------------------------------- rea -------------------------------------
redirect_to :action => "${1:index}"$end

------------------------------------- reai -------------------------------------
redirect_to :action => "${1:show}", :id => ${2:@item}$end

-------------------------------------- rec -------------------------------------
redirect_to :controller => "${1:items}"$end

------------------------------------- reca -------------------------------------
redirect_to :controller => "${1:items}", :action => "${2:list}"$end

------------------------------------- recai ------------------------------------
redirect_to :controller => "${1:items}", :action => "${2:show}", :id => ${3:@item}$end

-------------------------------------- rej -------------------------------------
reject { |${1:e}| $end }

------------------------------------ reject ------------------------------------
reject { |${1:element}| ${1:element}.$end }

-------------------------------------- req -------------------------------------
require "$end"

------------------------------------- reve -------------------------------------
reverse_each { |${1:e}| $end }

-------------------------------------- rf --------------------------------------
render :file => "${1:filepath}"$end

-------------------------------------- rfu -------------------------------------
render :file => "${1:filepath}", :use_full_path => ${2:false}$end

-------------------------------------- ri --------------------------------------
render :inline => "${1:<%= 'hello' %>}"$end

-------------------------------------- ril -------------------------------------
render :inline => "${1:<%= 'hello' %>}", :locals => { ${2::name} => "${3:value}"$4 }$end

-------------------------------------- rit -------------------------------------
render :inline => "${1:<%= 'hello' %>}", :type => ${2::rxml}$end

-------------------------------------- rl --------------------------------------
render :layout => "${1:layoutname}"$end

-------------------------------------- rn --------------------------------------
render :nothing => ${1:true}$end

-------------------------------------- rns -------------------------------------
render :nothing => ${1:true}, :status => ${2:401}$end

-------------------------------------- rp --------------------------------------
render :partial => "${1:item}"$end

-------------------------------------- rpc -------------------------------------
render :partial => "${1:item}", :collection => ${2:items}$end

-------------------------------------- rpl -------------------------------------
render :partial => "${1:item}", :locals => { :${2:name} => "${3:value}"$4 }$end

-------------------------------------- rpo -------------------------------------
render :partial => "${1:item}", :object => ${2:object}$end

-------------------------------------- rps -------------------------------------
render :partial => "${1:item}", :status => ${2:500}$end

-------------------------------------- rt --------------------------------------
render :text => "${1:text to render...}"$end

-------------------------------------- rtl -------------------------------------
render :text => "${1:text to render...}", :layout => "${2:layoutname}"$end

------------------------------------- rtlt -------------------------------------
render :text => "${1:text to render...}", :layout => ${2:true}$end

-------------------------------------- rts -------------------------------------
render :text => "${1:text to render...}", :status => ${2:401}$end

-------------------------------------- rw --------------------------------------
attr_accessor :${1:attr_names}

--------------------------------------- s --------------------------------------
session[:${1:user}]$end

-------------------------------------- sca -------------------------------------
scan(/${1:pattern}/) { |${2:match}| $end }

-------------------------------------- sel -------------------------------------
select { |${1:e}| $end }

------------------------------------ select ------------------------------------
select { |${1:element}| ${1:element}.$2 }$end

-------------------------------------- sor -------------------------------------
sort { |a, b| $end }

------------------------------------- sorb -------------------------------------
sort_by { |${1:e}| $end }

-------------------------------------- tc --------------------------------------
require "test/unit"

require "${1:library_file_name}"

deli    delete_if { |${1:e}| $end }

-------------------------------------- ts --------------------------------------
require "test/unit"

require "tc_${1:test_case_file}"
require "tc_${2:test_case_file}"

-------------------------------------- uni -------------------------------------
ARGF.each_line$1 do |${2:line}|
    $end
end

------------------------------------ unless ------------------------------------
unless ${1:condition}
    $end
end

------------------------------------- usai -------------------------------------
if ARGV.$1
  puts "Usage:  #{\$PROGRAM_NAME} ${2:ARGS_GO_HERE}"
  exit
end

------------------------------------- usau -------------------------------------
unless ARGV.$1
  puts "Usage:  #{\$PROGRAM_NAME} ${2:ARGS_GO_HERE}"
  exit
end

-------------------------------------- va --------------------------------------
validates_associated :${1:attribute}, :on => "${2:create}"$end

------------------------------------- vaif -------------------------------------
validates_associated :${1:attribute}, :on => "${2:create}", :if => proc { |obj| ${3:obj.condition?} }$end

-------------------------------------- vc --------------------------------------
validates_confirmation_of :${1:attribute}, :on => "${2:create}", :message => "${3:should match confirmation}"$end

------------------------------------- vcif -------------------------------------
validates_confirmation_of :${1:attribute}, :on => "${2:create}", :message => "${3:should match confirmation}", :if => proc { |obj| ${4:obj.condition?} }$end

-------------------------------------- ve --------------------------------------
validates_exclusion_of :${1:attribute}, :in => ${2:enumerable}, :on => "${3:create}", :message => "${4:is not allowed}"$end

------------------------------------- veif -------------------------------------
validates_exclusion_of :${1:attribute}, :in => ${2:enumerable}, :on => "${3:create}", :message => "${4:is not allowed}", :if => proc { |obj| ${5:obj.condition?} }$end

------------------------------------ verify ------------------------------------
verify :only => [:$1], :method => :post, :render => {:status => 500, :text => "use HTTP-POST"}$end

------------------------------------ verifyr -----------------------------------
verify :only => [:$1], :session => :user, :params => :id, :redirect_to => {:action => '${2:index}'}$end

------------------------------------- vlen -------------------------------------
validates_length_of ${1::name}, :maximum=>${2:10}, :message=>"${3:less than %d if you don't mind}"

-------------------------------------- vp --------------------------------------
validates_presence_of :${1:attribute}, :on => "${2:create}", :message => "${3:must be present}"$end

------------------------------------- vpif -------------------------------------
validates_presence_of :${1:attribute}, :on => "${2:create}", :message => "${3:must be present}", :if => proc { |obj| ${4:obj.condition?} }$end

-------------------------------------- vu --------------------------------------
validates_uniqueness_of :${1:attribute}, :on => "${2:create}", :message => "${3:must be unique}"$end

------------------------------------- vuif -------------------------------------
validates_uniqueness_of :${1:attribute}, :on => "${2:create}", :message => "${3:must be unique}", :if => proc { |obj| ${4:obj.condition?} }$end

--------------------------------------- w --------------------------------------
attr_writer :${1:attr_names}

------------------------------------- when -------------------------------------
when ${1:condition}
    $end

------------------------------------- ydump ------------------------------------
File.open(${1:"${2:path/to/file}.yaml"}, "w") { |${3:file}| YAML.dump(${4:obj}, ${3:file}) }

------------------------------------- yload ------------------------------------
File.open(${1:"${2:path/to/file}.yaml"}) { |${3:file}| YAML.load(${3:file}) }

-------------------------------------- zip -------------------------------------
zip(${1:enums}) { |${2:row}| $end }

Posted in , ,  | Tags , , , ,  | 8 comments | no trackbacks

Older posts: 1 ... 5 6 7 8 9 ... 11