Re-run a modified ActiveRecord Migration

Rails has this cool feature called ActiveRecord Migration. It’s an easy way to create and update your database. But if you get it wrong it is super annoying. This is where migrations and me loathe each other.

I had a migration which had the wrong column names. Now I could easily create another migration to change the names, but I’m still developing and I don’t want yet another migration. First thing I did was modified the migration before rolling things back. That is definitely going to fail. SO I reverted my changes.

class AddRhevParams < ActiveRecord::Migration
  def change
    remove_column :fusor_deployments, :rhev_params
    add_column :fusor_deployments, :rhev_hypervisor_host_id, :integer
    add_column :fusor_deployments, :rhev_engine_host_id, :integer
    add_column :fusor_deployments, :rhev_hypervisor, :string
    add_column :fusor_deployments, :rhev_engine, :string
    add_column :fusor_deployments, :rhev_database, :string
    add_column :fusor_deployments, :rhev_cluster, :string
    add_column :fusor_deployments, :rhev_storage, :string
    add_column :fusor_deployments, :rhev_cpu, :string
    add_column :fusor_deployments, :rhev_share, :string
  end
end

First I tried rake db:rollback STEP=1 hoping that it would put the DB in the state prior to my latest migration. For whatever reason it failed with an IrreversableMigration error.

$ rake db:rollback STEP=1
The Apipie cache is turned off. Enable it and run apipie:cache rake task to speed up API calls.
Workaround for RbVmomi may not work as ComputeResource is already loaded: ComputeResource
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = fal
se to avoid this message.
==  AddRhevParams: reverting ==================================================
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

ActiveRecord::IrreversibleMigration/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:42:in `block in inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `map'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:401:in `block (3 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:358:in `revert'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:400:in `block (2 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:399:in `block in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/connection_pool.rb:129:in `with_connection'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:389:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:528:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:720:in `block (2 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `call'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `block in ddl_transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/transactions.rb:208:in `transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `ddl_transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:719:in `block in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:700:in `each'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:700:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:574:in `down'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:656:in `move'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:562:in `rollback'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/railties/databases.rake:276:in `block (2 levels) in '
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `eval'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `'
caused by: (ActiveRecord::IrreversibleMigration) ActiveRecord::IrreversibleMigration
    ... skipped 46 lines
ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:42:in `block in inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `map'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:401:in `block (3 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:358:in `revert'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:400:in `block (2 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:399:in `block in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/connection_pool.rb:129:in `with_connection'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:389:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:528:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:720:in `block (2 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `call'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `block in ddl_transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/transactions.rb:208:in `transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:775:in `ddl_transaction'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:719:in `block in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:700:in `each'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:700:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:574:in `down'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:656:in `move'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:562:in `rollback'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/railties/databases.rake:276:in `block (2 levels) in '
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `eval'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `'
Tasks: TOP => db:rollback
(See full trace by running task with --trace)

Then I tried rake db:migrate:down --trace VERSION= and that failed with the same error.

$ rake db:migrate:down --trace VERSION=20150216190947
** Invoke db:migrate:down (first_time)
** Invoke environment (first_time)
** Execute environment
The Apipie cache is turned off. Enable it and run apipie:cache rake task to speed up API calls.
Workaround for RbVmomi may not work as ComputeResource is already loaded: ComputeResource
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = fal
se to avoid this message.
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:migrate:down
==  AddRhevParams: reverting ==================================================
rake aborted!
ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:42:in `block in inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `map'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration/command_recorder.rb:40:in `inverse'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:401:in `block (3 levels) in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:358:in `revert'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:400:in `block (2 levels) in migrate'
/home/vagrant/.rvm/rubies/ruby-1.9.3-p448/lib/ruby/1.9.1/benchmark.rb:280:in `measure'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:399:in `block in migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/connection_pool.rb:129:in `with_connection'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:389:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:528:in `migrate'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:679:in `run'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/migration.rb:578:in `run'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-3.2.21/lib/active_record/railties/databases.rake:238:in `block (3 levels) in '
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:240:in `call'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:240:in `block in execute'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:235:in `each'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:235:in `execute'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:179:in `block in invoke_with_call_chain'
/home/vagrant/.rvm/rubies/ruby-1.9.3-p448/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:172:in `invoke_with_call_chain'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/task.rb:165:in `invoke'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:150:in `invoke_task'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:106:in `each'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:106:in `block in top_level'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:115:in `run_with_threads'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:100:in `top_level'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:78:in `block in run'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:176:in `standard_exception_handling'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/lib/rake/application.rb:75:in `run'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/gems/rake-10.4.2/bin/rake:33:in `'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/bin/rake:19:in `load'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448@global/bin/rake:19:in `'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `eval'
/home/vagrant/.rvm/gems/ruby-1.9.3-p448/bin/ruby_executable_hooks:15:in `'
Tasks: TOP => db:migrate:down

Doing some googling, I found you can run rails console and run the migrations by hand, see Executing Migration Commands From Rails Console.

I then ran these commands in my rails console:

ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_hypervisor_host_id
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_engine_host_id
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_hypervisor
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_engine
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_database
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_cluster
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_storage
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_cpu
ActiveRecord::Migration.remove_column :fusor_deployments, :rhev_share
ActiveRecord::Migration.add_column :fusor_deployments, :rhev_params, :text

Then I went into the database and deleted the reference to my migration from the schema_migrations.

=# delete from schema_migrations where version = '20150216190947';

That cleaned up the migration and I was able to rerun using rake db:migrate

class AddRhevParams < ActiveRecord::Migration
  def change
    remove_column :fusor_deployments, :rhev_params
    add_column :fusor_deployments, :rhev_hypervisor_host_id, :integer
    add_column :fusor_deployments, :rhev_engine_host_id, :integer
    add_column :fusor_deployments, :rhev_hypervisor_hostname, :string
    add_column :fusor_deployments, :rhev_engine_hostname, :string
    add_column :fusor_deployments, :rhev_database_name, :string
    add_column :fusor_deployments, :rhev_cluster_name, :string
    add_column :fusor_deployments, :rhev_storage_name, :string
    add_column :fusor_deployments, :rhev_storage_type, :string
    add_column :fusor_deployments, :rhev_storage_address, :string
    add_column :fusor_deployments, :rhev_cpu_type, :string
    add_column :fusor_deployments, :rhev_share_path, :string
  end
end

So if you have trouble rolling things back automatically, you can try using rails console and mucking with the schema_migrations database table.

Ten years!

Ten years ago today I joined Red Hat’s Satellite team. It’s been amazing to see the company grow from 850 people to well over 6000. Lately I’ve been working on the
Candlepin project.

Eclipse you suck!

I’ve spent several hours trying to get the Android Developer Toolkit version of eclipse to run on Fedora 20. It’s constantly keeps dying with a segfault and libsoup.

[13899.331278] java[15520]: segfault at 0 ip 0000003929c70061 sp 00007fb66bd4df40 error 4 in libsoup-2.4.so.1.7.0[3929c00000+a8000]

I’ve tried all the workarounds on the net which suggest to make it use mozilla, so far no luck.

-Dorg.eclipse.swt.browser.DefaultType=mozilla
-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib64/xulrunner/

I even deleted my $HOME/.eclipse, that didn’t work. I switched to a new workspace specifically for ADT. Again didn’t help, still constantly dies.

UGH! Piece of Garbage. And to Ajay, no IntelliJ comments 🙂

tmux window name

Here’s how I got my tmux windows to be named ‘short hostname:working directory’.

Create a bash function in your $HOME/.bashrc.

settitle ()
{
    # only run this in tmux
    case "$TERM" in
        screen*)
            # get hostname without FQDN
            H=`hostname -s`
            # get current directory without the full path
            D=`basename $PWD`
            # set title of tmux/screen window
            printf "33k$H:$D33\\"
            ;;
    esac
}

Then call settitle from your PS1 prompt variable:

PS1="...(settitle)\$ "

Here is my current prompt:

PS1="[\u@\h \W\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')]\$(settitle)\$ " 

what tag is this commit in?

Thanks to jbowes for saving me time with git. I had a commit and wanted to find out what tag it was in. I tended to do the opposite, given a tag is this commit in it. And boy did I do it the hard way:

git checkout TAG
git checkout -b TAG # assuming there wasn't one already
tig # search for the commit in the list

That was cumbersome and a bit error prone. Now with a simple command I can find the tag more easily.

git describe --contains COMMIT_SHA

Setting primary display

When at home I hook up the laptop to an external monitor. For the most part it works great, plug it in and it works. I want the external monitor to be the primary. Pretty simple with xrandr --output DISPLAY --primary so I adapted my vij command to list out the monitors and present the list to me.

#!/bin/sh

monitors=$(xrandr | grep " connected " | awk '{print $1}')
matches=$(echo $monitors | gawk '{print NF}')

case "$matches" in
    0)
       echo "No matches found"
       show=""
       ;;
    1)
       show=$monitors
       ;;
    *)
       echo
       echo "Multiple matches found..."
       i=1
       for option in $monitors
       do
          echo "$i: $option"
          i=`expr $i + 1`
       done
       echo "q: Quit"
       echo
       read -p "? " ans
       if [ "q" == "$ans" ]; then
          show=""
       else
          show=$(echo $monitors | gawk '{print $'$ans'}')
       fi
       ;;
esac

if [ "" != "$show" ]; then
   xrandr --output $show --primary
fi

When you run the script this is what you see:

$ bin/prim 

Multiple matches found...
1: LVDS1
2: VGA1
q: Quit

? 

Gnome 3 why do you hate me?

So my Gnome 3 (gnome-shell, Fedora 18) went to screensaver. When I unlocked it this is what my desktop looks like.

lockup

How do I get out of this? My machine is running fine. I can ssh into it and it’s running normal but I can’t interact with the desktop. Quite annoying.

Gnome 3 and themes are a disappointment

Getting a nice dark theme on a Linux desktop is probably one of the most difficult things I’ve had to do on a computer. I can install a machine from scratch, get virtualized systems working, connect to a SAMBA share, even get my Linux desktop to talk to a network printer. But can I get a dark network theme across the board? No way!

I recently upgraded from Fedora 16 to Fedora 18. Well not really an upgrade, more of a fresh install of Fedora 18 but kept my existing home directory. I enabled dark themes using the Gnome Tweak Tool. I thought this would take care of it but it only seems to work for some applications: Evolution, Nautilus, Gnome Terminal all seem to look just fine. But anything not included with Gnome 3 looks like rubbish.

Here’s the Gnome Tweak Tool. It looks pretty nice in a dark theme.
theme-tweaktool

As does the Gnome Terminal, especially with the transparency on the window.
dark-terminal

But any other application is horrendous. Checkout Google Chrome on the same machine.

theme-chrome

And Thunderbird is also a mess, I was able to get the message window be white on black, but there is no easy way to change the look and feel of Thunderbird. All of the themes are ‘cutesy’ trying to add splash graphics and what not. See Thunderbird themes for examples.

theme-thunderbird

And XChat is unusable at the moment with the white text on white background in the text entry box.

xchat-white

I’ve gotten to the point where I gave up on Thunderbird and XChat and started using mutt and irssi since the terminal theme works. But I have to say mutt is ok but I prefer Thunderbird’s folders and notifications. irssi is actually quite workable so if I can’t get the themes fixed in XChat I can live with irssi.

But this really shouldn’t be that difficult, why do app designer think people want whitewashed apps, it’s 2013 and we still can’t get themes working.

</rant>

Apply github pull requests as patches

Our team uses Github’s pull requests as a code review process, which requires a fair number of requests that need to be tested. Github has a really cool feature where if you put the .patch extension to the url it will show you a diff that can be passed to git am.

So given a pull request 162 (https://github.com/candlepin/candlepin/pull/162) you can use curl to download the patch and then pipe it to git am. Once you’re done reviewing, simply revert your branch back using git reset --hard origin/master.

I added some functions to my .bashrc for the projects I review most often, here’s the snippet:

# apply the given pull request from the given project as a patch
# arg: project (i.e. candlepin/subscription-manager/etc
# arg: pull request number
__github ()
{
    curl https://github.com/candlepin/$1/pull/$2.patch | git am
}

# apply the given pull request for candlepin
# arg: pull request number
cppull ()
{
    __github "candlepin" $1
}

# apply the given pull request for subscription-manager
# arg: pull request number
submanpull ()
{
    __github "subscription-manager" $1
}

Summary