contao 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -7,7 +7,6 @@ rvm:
7
7
  matrix:
8
8
  allow_failures:
9
9
  - rvm: rbx-19mode # We have some failing specs we need to take care of
10
- - rvm: jruby-19mode # This is more complicated as we need the oily_ong gem but it doesn't work for Jruby
11
10
  notifications:
12
11
  recipients:
13
12
  - wael.nasreddine@gmail.com
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in contao.gemspec
4
4
  gemspec
5
+
6
+ group :development, :test do
7
+ gem 'binding_of_caller', :platforms => [:mri_19, :rbx]
8
+ end
data/README.md CHANGED
@@ -5,24 +5,69 @@ Welcome to Contao!
5
5
  Status](https://secure.travis-ci.org/TechnoGate/contao.png?branch=master)](http://travis-ci.org/TechnoGate/contao)
6
6
  [![Gemnasium](https://gemnasium.com/TechnoGate/contao.png)](https://gemnasium.com/TechnoGate/contao)
7
7
 
8
- This gem will help you to quickly generate an application using [Contao
9
- CMS](http://www.contao.org/en/) which has pre-built support for Sass,
10
- Compass, CoffeeScript, Jasmine and Capistrano.
8
+ This gem will help you to quickly develop a website using [Contao
9
+ CMS](http://www.contao.org/en/) which has pre-built support for
10
+ [Sass](http://sass-lang.com), [Compass](http://compass-style.org),
11
+ [CoffeeScript](http://coffeescript.org),
12
+ [Jasmine](http://pivotal.github.com/jasmine/) and
13
+ [Capistrano](https://github.com/capistrano/capistrano).
11
14
 
12
15
  It also feature hashed assets served by the [Contao Assets
13
16
  extension](https://github.com/TechnoGate/contao_assets), which allows you
14
17
  to have an md5 appended to each of your assets URL on the production
15
18
  site.
16
19
 
17
- The integration with Capistrano allows you to quickly deploy, copy
18
- assets, import database and even upload media such as images and PDFs
19
- all from the command line using Capistrano.
20
+ ```html
21
+ <link rel="stylesheet" type="text/css" href="/resources/application-c6e2457d9ccce0f344c50e5bcc12fcdc.css" />
22
+ <script type="text/javascript" src="/resources/application-327af3660470fb1c3f8e6593670cfc1e.js"></script>
23
+ ```
24
+
25
+ All the images references by the CSS file, are also hashed, so when you
26
+ deploy a new version of your image and/or your CSS, you are absolutely
27
+ sure that your visitors do not get a cached copy of your old asset.
28
+
29
+ ```sass
30
+ div
31
+ +background(image-url('body.jpg') no-repeat top center)
32
+ ```
33
+
34
+ Would generate:
35
+
36
+ ```css
37
+ div {
38
+ background: url(/resources/body-fc4a0f5f0b0f9ceec32bde5d15928467.jpg) no-repeat top center;
39
+ }
40
+ ```
41
+
42
+ Compass gives great power over your CSS, one most-wanted feature is the
43
+ sprites, so having one PNG file for all your backgrounds is just
44
+ awesome, the generate CSS looks like this
45
+
46
+ ```sass
47
+ div
48
+ +background(sprite($background, body))
49
+ ```
50
+
51
+ Would generate:
52
+
53
+ ```css
54
+ div {
55
+ background: url(/resources/background-sbd69a8307b-a00c8f7a8536397c6279726316eae16f.png) 0 -3089px;
56
+ }
57
+ ```
58
+
59
+ Check [Compass Sprites
60
+ Documentation](http://compass-style.org/help/tutorials/spriting)
61
+
62
+ Finally, the integration with Capistrano allows you to quickly deploy,
63
+ copy assets, import database and even upload media such as images and
64
+ PDFs all from the command line using Capistrano.
20
65
 
21
66
  ## Pre-requisites
22
67
 
23
68
  Before installing the gem, you need to make you are running on a Ruby
24
69
  version 1.9.2 or greater as this Gem and most of it's dependencies do
25
- not support Ruby 1.8, to check the version you are running, uee the
70
+ not support Ruby 1.8, to check the version you are running, using the
26
71
  following command:
27
72
 
28
73
  ```bash
@@ -33,6 +78,17 @@ If you're running a ruby version below 1.9, please install a 1.9 version
33
78
  by following the guide at the [Rbenv
34
79
  Installer](https://github.com/fesplugas/rbenv-installer) project.
35
80
 
81
+ Contao depends on Qt (for headless javascript testing using
82
+ [jasmine](https://github.com/pivotal/jasmine) and
83
+ [jasmine-headless-webkit](http://johnbintz.github.com/jasmine-headless-webkit),
84
+ to install it, refer to [Capybara Webkit
85
+ Installation](https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit)
86
+
87
+ And Finally, you need [Git](http://git-scm.com) as the whole template
88
+ has been built for it, you can also work with a different SCM, but you
89
+ need to get the template file yourself, initialize it and track it with
90
+ your favorite SCM, something I strongly vote against.
91
+
36
92
  ## Installation
37
93
 
38
94
  Install contao with the following command
@@ -45,6 +101,18 @@ Don't forget to run `rbenv rehash` if you are using
45
101
  **[rbenv](https://github.com/sstephenson/rbenv)** as this gem provides
46
102
  an executable.
47
103
 
104
+ ## Database name
105
+
106
+ Locally, the database name is the same as the application name, so if
107
+ you named your project is named **my_project**, the database name will be
108
+ named **my_project**.
109
+
110
+ On the server, Capistrano will append the environment on which the
111
+ deployment occured (check the deployment section below for more
112
+ information) to the application name, so if your project is named
113
+ **my_project** and you are deployment to the staging environment, the
114
+ database name would default to **my_project_staging**
115
+
48
116
  ## Usage
49
117
 
50
118
  ### Generating a config file
@@ -66,7 +134,7 @@ Generating a new project is very easy, just use the following command:
66
134
  contao new /path/to/my_project
67
135
  ```
68
136
 
69
- This command will generate an application called `my\_project` in the
137
+ This command will generate an application called `my_project` in the
70
138
  folder `/path/to`, the application name is very important as it defaults
71
139
  to the name of your database, check the [Database name](#database-name)
72
140
  section below for more information.
@@ -89,25 +157,165 @@ detailed information
89
157
  ### Work on the project
90
158
 
91
159
  To be able to develop with this version of Contao, you first need to
92
- understand how it actually works.
160
+ understand how it actually works, take a look at the [project
161
+ structure](#project-structure) for more information on how files are
162
+ organised
93
163
 
94
- TODO: Continue this section
164
+ Contao is integrated with Rails, actually only the asset pipeline
165
+ functionality is being used, Compass is also integrated with the project
166
+ so you can develop your CSS using Compass functions and mixins as well
167
+ as sprites.
95
168
 
96
- ## Database name
169
+ To start working on the project, you need to run the rails server by
170
+ running
97
171
 
98
- Locally, the database name is the same as the application name, so if
99
- you named your project is named **my_project**, the database name will be
100
- named **my_project**.
172
+ ```bash
173
+ bundle exec foreman start
174
+ ```
101
175
 
102
- On the server, Capistrano will append the environment on which the
103
- deployment occured (check the deployment section below for more
104
- information) to the application name, so if your project is named
105
- **my_project** and you are deployment to the staging environment, the
106
- database name would default to **my_project_staging**
176
+ This will start a rails process on port **9876** and serve the assets
177
+ from their, The [Contao Assets
178
+ Extension](https://github.com/TechnoGate/contao_assets) automatically
179
+ detect that you are running in development and will use assets from the
180
+ rails server directly.
107
181
 
108
182
  ## Deploying
109
183
 
110
- TODO: Write this section
184
+ ### Introduction
185
+
186
+ Before deploying the project, you need to edit Capistrano config files
187
+ located at `config/deploy.rb` and `config/deploy/development.rb`.
188
+
189
+ For a standard project tracked by [Git](http://git-scm.com), you do not
190
+ need to edit the file `config/deploy.rb` but you **do need** to edit
191
+ `config/deploy/development.rb` which is auto-documented.
192
+
193
+ ### Multistage
194
+
195
+ Capistrano comes with support for multiple stages, you could have
196
+ `development`, `staging`, `production` or any other stage, all you need
197
+ to have is the stage name mentioned in `config/deploy.rb`
198
+
199
+ ```ruby
200
+ set :stages, ['development', 'staging', 'production']
201
+ ```
202
+
203
+ and a config file by the same name of the stage located at
204
+ `config/deploy/`, the Template is pre-configured for **development**,
205
+ **staging** and **production** but comes with only one config file
206
+ for **development**, to configure another stage just duplicate the
207
+ **development** file to the desired stage.
208
+
209
+ ### Deploying
210
+
211
+ #### Provisioning the server
212
+
213
+ To deploy your project, you need to first configure the server, if you
214
+ are deploying to a server managed by yourself and using **Nginx**
215
+ (Apache templates will be added later), you can generate a config file
216
+ for your new site, add a user to the database and create the database
217
+ using the following command:
218
+
219
+ ```shell
220
+ bundle exec cap development deploy:server:setup
221
+ ```
222
+
223
+ The above step is optional and only useful if you manage your own
224
+ server, but if you are using a shared server (Hosting), running that
225
+ command would not be possible as you don't have root access.
226
+
227
+ NOTE: This command must be used only once per stage per project.
228
+
229
+ #### Setup the project
230
+
231
+ Before deploying you need to setup the project structure, generate the
232
+ **localconfig.php**, and the **.htaccess**, to do that just run
233
+
234
+ ```shell
235
+ bundle exec cap development deploy:setup
236
+ ```
237
+
238
+ NOTE: This command must be used only once per stage per project.
239
+
240
+ #### Deploy the project
241
+
242
+ To deploy the project simply run
243
+
244
+ ```shell
245
+ bundle exec cap development deploy
246
+ ```
247
+
248
+ #### Multistage
249
+
250
+ As you may have noticed, all the above commands had the **development**
251
+ stage as the first argument (first argument to **cap**), to deploy to
252
+ any other stage just use that stage's name instead.
253
+
254
+ The contao template comes pre-configured with **development** as the
255
+ **default stage**
256
+
257
+ ```ruby
258
+ set :default_stage, :development
259
+ ```
260
+
261
+ So if you omit the stage (the first argument) when calling **cap**
262
+
263
+ ```shell
264
+ bundle exec cap deploy
265
+ ```
266
+
267
+ The stage would be set to whatever `default_stage` is set to, in this
268
+ case **development**
269
+
270
+ ## Useful Capistrano Tasks
271
+
272
+ ### Database import/export
273
+
274
+ You do not need to use SSH or phpMyAdmin to export or import a database
275
+ dump, in fact capistrano already knows the credentials to access the
276
+ database, so We added a few tasks to help ease this process.
277
+
278
+ #### Importing a database dump
279
+
280
+ To import a database dump, which is very useful for deploying a website
281
+ that was in development on your localhost, you can use the task
282
+ `db:import` (Remember that all the following commands can
283
+ optionally take the stage as the first argument, when omited the stage
284
+ is set to the `default_stage`). To import `/path/to/project.sql` for
285
+ example, you only have to use the command
286
+
287
+ ```shell
288
+ bundle exec cap db:import /path/to/project.sql
289
+ ```
290
+
291
+ The above command will first backup your database on the server (check
292
+ `/backups` relative to your project directory of course), and then
293
+ import the project.sql into it, however it's up to you to make sure your
294
+ SQL dump file has `DROP TABLE IF EXISTS` statements to overwrite the
295
+ tables.
296
+
297
+ #### Exporting a database dump
298
+
299
+ To export a database dump, which is very useful to import changes from
300
+ the server to your local development machine
301
+
302
+ ```shell
303
+ bundle exec cap db:export
304
+ ```
305
+
306
+ This file will download the SQL dump file to a random file in `/tmp` but
307
+ you can optionally give it an argument which would be used as a
308
+ filename. This command will also backup the database on the server.
309
+
310
+ ### Content import/export
311
+
312
+ #### Importing content
313
+
314
+ TODO: Write this section.
315
+
316
+ #### Exporting content
317
+
318
+ TODO: Write this section.
111
319
 
112
320
  ## Contributing
113
321
 
@@ -126,13 +334,133 @@ Or
126
334
  For bugs and feature request, please use __Github issues__, for other
127
335
  requests, you may use:
128
336
 
129
- - [Google Groups](http://groups.google.com/group/janus-vimius)
130
- - [Github private message](https://github.com/inbox/new/eMxyzptlk)
131
337
  - Email: [contact@technogate.fr](mailto:contact@technogate.fr)
132
338
 
133
339
  Don't forget to follow me on [Github](https://github.com/eMxyzptlk) and
134
340
  [Twitter](https://twitter.com/eMxyzptlk) for news and updates.
135
341
 
342
+ ## Credits
343
+
344
+ I'd like to give a special thanks to the guys at [Agence
345
+ Durable](http://www.agence-durable.com) for supporting and testing this
346
+ project, [Leo Feyer](https://github.com/leofeyer) for creating an
347
+ awesome and easy to use CMS, and of course all Contao contributors, and
348
+ Finally The [Rails Core Team](http://rubyonrails.org/core) and the
349
+ entire Ruby community which are simply **awesome**.
350
+
351
+ ## TODO
352
+
353
+ * The `contao` folder is a mess and can become a lot messier pretty
354
+ quickly so we need to extract each extension into it's folder (make it
355
+ modularized)
356
+ * Add Capistrano tasks for Starting/Stopping/Reloading Nginx
357
+ * Add Capistrano support for Apache
358
+ * The install password should be different for each website and the
359
+ developer should be able to modify it, Basically the install password
360
+ in `~/.contao/config.yml` should be stored in clear-text (or encrypted
361
+ but decryptable), and once we generate a new project we should generate
362
+ a new salt, store it in an initializer, generate the install password
363
+ and store it in an initializer as well
364
+ * A new rake task should be created to help the user easly modify the
365
+ install password
366
+ * The **encryption_key** should be generated for each project and not
367
+ stored in the `~/.contao/config.yml`
368
+ * The assets takes a considerably amount of time to be uploaded,
369
+ specially if you have many images, we should compress the entire
370
+ folder, upload it and then extract it on the server, as it would be
371
+ much faster to upload one file.
372
+
373
+ ## Project structure
374
+
375
+ ```shell
376
+ contao_template
377
+ ├── Capfile
378
+ ├── Gemfile
379
+ ├── Procfile
380
+ ├── Rakefile
381
+ ├── app
382
+ │   └── assets
383
+ │   ├── images
384
+ │   ├── javascripts
385
+ │   │   └── application.js
386
+ │   └── stylesheets
387
+ │   ├── application.css.sass
388
+ │   ├── definitions
389
+ │   │   ├── _all.sass
390
+ │   │   ├── _mixins.sass
391
+ │   │   ├── _sprites.sass
392
+ │   │   └── _variables.sass
393
+ │   └── thirdparty
394
+ │   └── _pie.sass
395
+ ├── config
396
+ │   ├── application.rb
397
+ │   ├── boot.rb
398
+ │   ├── compass.rb
399
+ │   ├── deploy
400
+ │   │   ├── development.rb
401
+ │   │   └── production.rb
402
+ │   ├── deploy.rb
403
+ │   ├── environment.rb
404
+ │   ├── environments
405
+ │   │   ├── development.rb
406
+ │   │   ├── production.rb
407
+ │   │   └── test.rb
408
+ │   ├── examples
409
+ │   │   └── localconfig.php.erb
410
+ │   ├── initializers
411
+ │   │   ├── secret_token.rb
412
+ │   │   └── session_store.rb
413
+ │   └── routes.rb
414
+ ├── config.ru
415
+ ├── contao
416
+ │   ├── plugins
417
+ │   ├── system
418
+ │   │   ├── drivers
419
+ │   │   ├── libraries
420
+ │   │   │   ├── Spyc.php -> ../../../lib/contao/libraries/spyc/spyc.php
421
+ │   │   └── modules
422
+ │   │   ├── BackupDB
423
+ │   │   ├── assets
424
+ │   │   ├── efg
425
+ │   │   ├── favicon
426
+ │   │   ├── googleanalytics
427
+ │   │   ├── listing
428
+ │   │   ├── parentslist
429
+ │   │   ├── subcolumns
430
+ │   │   ├── template-override
431
+ │   │   ├── videobox
432
+ │   │   └── videobox_vimeo
433
+ ├── lib
434
+ │   ├── assets
435
+ │   │   └── javascripts
436
+ │   │   ├── form_default_values
437
+ │   │   │   ├── autoload.js.coffee
438
+ │   │   │   └── main.js.coffee
439
+ │   │   ├── form_default_values.js
440
+ │   │   └── slider.js.coffee
441
+ │   ├── contao
442
+ │   │   └── libraries
443
+ │   │   └── spyc
444
+ │   └── tasks
445
+ ├── public
446
+ ├── script
447
+ │   └── rails
448
+ ├── spec
449
+ │   └── javascripts
450
+ │   ├── fixtures
451
+ │   │   └── slider.html
452
+ │   ├── helpers
453
+ │   │   └── spec_helper.js.coffee
454
+ │   ├── slider_spec.coffee
455
+ │   └── support
456
+ │   └── jasmine.yml
457
+ └── vendor
458
+ └── assets
459
+ ├── javascripts
460
+ └── stylesheets
461
+ ```
462
+
463
+
136
464
  ## License
137
465
 
138
466
  ### This code is free to use under the terms of the MIT license.
data/contao.gemspec CHANGED
@@ -4,10 +4,10 @@ require File.expand_path('../lib/contao/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ['Wael Nasreddine']
6
6
  gem.email = ['wael.nasreddine@gmail.com']
7
- gem.description = %q{Contao Integration with Rails.}
7
+ gem.description = 'Contao Integration with Rails Asset Pipeline, Compass and Capistrano'
8
8
  gem.summary = <<-EOS
9
- This gem will help you to quickly generate an application using Contao
10
- CMS which has pre-built support for Sass, Compass, CoffeeScript, Jasmine
9
+ This gem will help you to quickly develop a website using Contao CMS
10
+ which has pre-built support for Sass, Compass, CoffeeScript, Jasmine
11
11
  and Capistrano.
12
12
 
13
13
  It also feature hashed assets served by the Contao Assets extension,
@@ -37,4 +37,6 @@ all from the command line using Capistrano.
37
37
  # Development dependencies
38
38
  gem.add_development_dependency 'rspec'
39
39
  gem.add_development_dependency 'fakefs'
40
+ gem.add_development_dependency 'pry'
41
+ gem.add_development_dependency 'pry-doc'
40
42
  end
@@ -1,101 +1,78 @@
1
1
  require 'yaml'
2
- require 'ostruct'
3
- require 'singleton'
4
2
  require 'contao/core_ext/object'
5
3
 
6
4
  module TechnoGate
7
5
  module Contao
8
- class Application < OpenStruct
9
- include Singleton
10
-
11
- def initialize
12
- parse_global_config
13
-
14
- super
15
- end
16
-
17
- def linkify
18
- exhaustive_list_of_files_to_link(
19
- Rails.root.join(config.contao_path),
20
- Rails.public_path
21
- ).each do |list|
22
- FileUtils.ln_s list[0], list[1]
6
+ class Application
7
+ class << self
8
+ def linkify
9
+ exhaustive_list_of_files_to_link(
10
+ Rails.root.join(config.contao.path),
11
+ Rails.public_path
12
+ ).each do |list|
13
+ FileUtils.ln_s list[0], list[1]
14
+ end
23
15
  end
24
- end
25
-
26
- def self.linkify
27
- instance.linkify
28
- end
29
-
30
- def name
31
- config.application_name || File.basename(Rails.root)
32
- end
33
-
34
- def self.name
35
- instance.name
36
- end
37
16
 
38
- def config
39
- Rails.application.config
40
- end
17
+ def global_config_path
18
+ "#{ENV['HOME']}/.contao/config.yml"
19
+ end
41
20
 
42
- def self.config
43
- instance.config
44
- end
21
+ def load_global_config!
22
+ config.contao.global =
23
+ YAML.load(File.read(global_config_path)).to_openstruct
24
+ end
45
25
 
46
- def self.default_global_config(options = {})
47
- {
48
- 'install_password' => '',
49
- 'encryption_key' => '',
50
- 'admin_email' => 'admin@example.com',
51
- 'time_zone' => 'Europe/Paris',
52
- 'mysql' => {
53
- 'host' => 'localhost',
54
- 'user' => 'root',
55
- 'pass' => 'toor',
56
- 'port' => 3306,
57
- },
58
- 'smtp' => {
59
- 'enabled' => false,
60
- 'host' => '',
61
- 'user' => '',
62
- 'pass' => '',
63
- 'ssl' => true,
64
- 'port' => 465,
65
- },
66
- }.merge(options)
67
- end
26
+ def config
27
+ Rails.application.config
28
+ end
68
29
 
69
- def global_config_path
70
- "#{ENV['HOME']}/.contao/config.yml"
71
- end
30
+ def name
31
+ config.contao.application_name || File.basename(Rails.root)
32
+ end
72
33
 
73
- protected
34
+ protected
74
35
 
75
- # Parse the global yaml configuration file
76
- def parse_global_config
77
- if File.exists? global_config_path
78
- config.contao_global_config = YAML.load(File.read(global_config_path)).to_openstruct
36
+ def default_global_config(options = {})
37
+ {
38
+ 'install_password' => '',
39
+ 'admin_email' => 'admin@example.com',
40
+ 'time_zone' => 'Europe/Paris',
41
+ 'mysql' => {
42
+ 'host' => 'localhost',
43
+ 'user' => 'root',
44
+ 'pass' => 'toor',
45
+ 'port' => 3306,
46
+ },
47
+ 'smtp' => {
48
+ 'enabled' => false,
49
+ 'host' => '',
50
+ 'user' => '',
51
+ 'pass' => '',
52
+ 'ssl' => true,
53
+ 'port' => 465,
54
+ },
55
+ }.merge(options)
79
56
  end
80
- end
81
57
 
82
- # Return an array of arrays of files to link
83
- #
84
- # @param [String] Absolute path to folder from which to link
85
- # @param [String] Absolute path to folder to which to link
86
- # @return [Array]
87
- def exhaustive_list_of_files_to_link(from, to)
88
- files = []
89
- Dir["#{from}/*"].each do |f|
90
- file = "#{to}/#{File.basename f}"
91
- if File.exists? file
92
- exhaustive_list_of_files_to_link(f, file).each { |f| files << f }
93
- else
94
- files << [f, file]
58
+ # Return an array of arrays of files to link
59
+ #
60
+ # @param [String] Absolute path to folder from which to link
61
+ # @param [String] Absolute path to folder to which to link
62
+ # @return [Array]
63
+ def exhaustive_list_of_files_to_link(from, to)
64
+ files = []
65
+ Dir["#{from}/*"].each do |f|
66
+ file = "#{to}/#{File.basename f}"
67
+ if File.exists? file
68
+ exhaustive_list_of_files_to_link(f, file).each { |f| files << f }
69
+ else
70
+ files << [f, file]
71
+ end
95
72
  end
96
- end
97
73
 
98
- files
74
+ files
75
+ end
99
76
  end
100
77
  end
101
78
  end
@@ -2,8 +2,8 @@ def print_help
2
2
  puts <<-EOH.gsub(/ [ ]+/, ' ')
3
3
  Welcome to Contao!
4
4
 
5
- This gem will help you to quickly generate an application using Contao
6
- CMS which has pre-built support for Sass, Compass, CoffeeScript, Jasmine
5
+ This gem will help you to quickly develop a website using Contao CMS
6
+ which has pre-built support for Sass, Compass, CoffeeScript, Jasmine
7
7
  and Capistrano.
8
8
 
9
9
  It also feature hashed assets served by the Contao Assets extension,
@@ -56,14 +56,6 @@ module TechnoGate
56
56
  def config_application_path
57
57
  "#{project_path}/config/application.rb"
58
58
  end
59
-
60
- def project_path
61
- @options[:path]
62
- end
63
-
64
- def project_name
65
- File.basename project_path
66
- end
67
59
  end
68
60
  end
69
61
  end
@@ -7,6 +7,16 @@ module TechnoGate
7
7
  end
8
8
 
9
9
  def generate; end
10
+
11
+ protected
12
+
13
+ def project_path
14
+ @options[:path]
15
+ end
16
+
17
+ def project_name
18
+ File.basename project_path
19
+ end
10
20
  end
11
21
  end
12
22
  end