janky 0.9.0 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +30 -0
- data/README.md +59 -21
- data/Rakefile +7 -0
- data/janky.gemspec +6 -4
- data/lib/janky.rb +81 -22
- data/lib/janky/app.rb +1 -1
- data/lib/janky/build.rb +9 -1
- data/lib/janky/builder/http.rb +8 -0
- data/lib/janky/builder/receiver.rb +1 -1
- data/lib/janky/{campfire.rb → chat_service.rb} +28 -55
- data/lib/janky/chat_service/campfire.rb +33 -0
- data/lib/janky/chat_service/hipchat.rb +29 -0
- data/lib/janky/chat_service/mock.rb +26 -0
- data/lib/janky/database/migrate/1312115512_init.rb +1 -1
- data/lib/janky/exception.rb +23 -0
- data/lib/janky/github.rb +82 -23
- data/lib/janky/github/api.rb +17 -8
- data/lib/janky/github/payload_parser.rb +1 -1
- data/lib/janky/hubot.rb +15 -14
- data/lib/janky/job_creator.rb +10 -2
- data/lib/janky/notifier/{campfire.rb → chat_service.rb} +4 -3
- data/lib/janky/notifier/mock.rb +2 -2
- data/lib/janky/public/css/base.css +33 -10
- data/lib/janky/repository.rb +4 -4
- data/lib/janky/templates/index.mustache +10 -5
- data/lib/janky/version.rb +1 -1
- data/lib/janky/views/index.rb +10 -3
- data/lib/janky/views/layout.rb +1 -0
- data/test/janky_test.rb +14 -2
- data/test/test_helper.rb +5 -4
- metadata +13 -10
- data/lib/janky/campfire/mock.rb +0 -0
data/CHANGES
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
= 0.9.9 / Not yet released
|
2
|
+
|
3
|
+
* HipChat support [Justin Smestad, Seth Chisamore, Simon Rozet]
|
4
|
+
|
5
|
+
* Support for GitHub Enterprise. [Dusty Burwell, Simon Rozet]
|
6
|
+
|
7
|
+
* Support for GitHub logins containing dashes. [Thom May]
|
8
|
+
|
9
|
+
* Support for branches containing slashes in their name. [Andres Torres]
|
10
|
+
|
11
|
+
* Respond with proper status code to invalid Jenkins notification
|
12
|
+
requests. [Chris Mytton]
|
13
|
+
|
14
|
+
* Support for Jenkins servers running behind SSL. [Vivek Pandey,
|
15
|
+
Gavin Heavyside, Thom May]
|
16
|
+
|
17
|
+
* Support for Jenkins servers running under a path [Piet Jaspers]
|
18
|
+
|
19
|
+
* Deprecate JANKY_CAMPFIRE_ACCOUNT. Please use JANKY_CHAT_CAMPFIRE_ACCOUNT
|
20
|
+
instead. [Simon Rozet]
|
21
|
+
|
22
|
+
* Deprecate JANKY_CAMPFIRE_TOKEN. Please use JANKY_CHAT_CAMPFIRE_TOKEN
|
23
|
+
instead. [Simon Rozet]
|
24
|
+
|
25
|
+
* Deprecate JANKY_CAMPFIRE_DEFAULT_ROOM. Please use
|
26
|
+
JANKY_CHAT_DEFAULT_ROOM instead. [Simon Rozet]
|
27
|
+
|
28
|
+
* Both JANKY_BASE_URL and JANKY_DEFAULT_BUILDER now require a trailing
|
29
|
+
slash. [Simon Rozet]
|
30
|
+
|
1
31
|
= 0.9 / 2011-12-19
|
2
32
|
|
3
33
|
* Initial public release.
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@ This is Janky, a continuous integration server built on top of
|
|
5
5
|
[Jenkins][], controlled by [Hubot][], and designed for [GitHub][].
|
6
6
|
|
7
7
|
* **Built on top of Jenkins.** The power, vast amount of plugins and large
|
8
|
-
|
8
|
+
community of the popular CI server all wrapped up in a great experience.
|
9
9
|
|
10
10
|
* **Controlled by Hubot.** Day to day operations are exposed as simple
|
11
11
|
Hubot commands that the whole team can use.
|
@@ -52,7 +52,7 @@ available rooms:
|
|
52
52
|
|
53
53
|
Then pick one:
|
54
54
|
|
55
|
-
hubot ci set janky
|
55
|
+
hubot ci set room janky The Serious Room
|
56
56
|
|
57
57
|
Get the status of a build:
|
58
58
|
|
@@ -86,21 +86,24 @@ instructions and install the [Notification Plugin][np] version 1.4.
|
|
86
86
|
|
87
87
|
Janky is designed to be deployed to [Heroku](https://heroku.com).
|
88
88
|
|
89
|
-
Grab all the necessary files from [
|
89
|
+
Grab all the necessary files from [the gist][gist]:
|
90
90
|
|
91
|
-
$ git clone
|
91
|
+
$ git clone git://gist.github.com/1497335 janky
|
92
92
|
|
93
93
|
Then push up it to a new Heroku app:
|
94
94
|
|
95
95
|
$ cd janky
|
96
96
|
$ heroku create --stack cedar
|
97
|
+
$ bundle install
|
98
|
+
$ git add Gemfile.lock
|
99
|
+
$ git commit Gemfile.lock -m "lock bundle"
|
97
100
|
$ git push heroku master
|
98
101
|
|
99
|
-
After
|
102
|
+
After configuring the app (see below), create the database:
|
100
103
|
|
101
104
|
$ heroku run rake db:migrate
|
102
105
|
|
103
|
-
[gist]: https://gist.github.com/
|
106
|
+
[gist]: https://gist.github.com/1497335
|
104
107
|
|
105
108
|
### Configuring
|
106
109
|
|
@@ -111,10 +114,11 @@ command:
|
|
111
114
|
|
112
115
|
Required settings:
|
113
116
|
|
114
|
-
* `JANKY_BASE_URL`: The application URL with a trailing slash. Example:
|
117
|
+
* `JANKY_BASE_URL`: The application URL **with** a trailing slash. Example:
|
115
118
|
`http://mf-doom-42.heroku.com/`.
|
116
|
-
* `JANKY_BUILDER_DEFAULT`: The Jenkins server URL with a trailing slash.
|
117
|
-
Example: `http://jenkins.example.com/`.
|
119
|
+
* `JANKY_BUILDER_DEFAULT`: The Jenkins server URL **with** a trailing slash.
|
120
|
+
Example: `http://jenkins.example.com/`. For basic auth, include the
|
121
|
+
credentials in the URL: `http://user:pass@jenkins.example.com/`.
|
118
122
|
* `JANKY_CONFIG_DIR`: Directory where build config templates are stored.
|
119
123
|
Typically set to `/app/config` on Heroku.
|
120
124
|
* `JANKY_HUBOT_USER`: Login used to protect the Hubot API.
|
@@ -124,11 +128,45 @@ Required settings:
|
|
124
128
|
* `JANKY_GITHUB_PASSWORD`: The password for the GitHub user.
|
125
129
|
* `JANKY_GITHUB_HOOK_SECRET`: Secret used to sign hook requests from
|
126
130
|
GitHub.
|
127
|
-
* `
|
128
|
-
|
131
|
+
* `JANKY_CHAT_DEFAULT_ROOM`: Chat room where notifications are sent by default.
|
132
|
+
|
133
|
+
#### GitHub Enterprise
|
134
|
+
|
135
|
+
Using Janky with [GitHub Enterprise][ghe] requires one extra setting:
|
136
|
+
|
137
|
+
* `JANKY_GITHUB_API_URL`: Full API URL of the instance, *with* a trailing
|
138
|
+
slash. Example: `https://github.example.com/api/v3/`.
|
139
|
+
|
140
|
+
[ghe]: https://enterprise.github.com
|
141
|
+
|
142
|
+
### Chat Notification
|
143
|
+
|
144
|
+
#### Campfire
|
145
|
+
Janky notifies [Campfire][] chat rooms by default. Required settings:
|
146
|
+
|
147
|
+
* `JANKY_CHAT_CAMPFIRE_ACCOUNT`: account name.
|
148
|
+
* `JANKY_CHAT_CAMPFIRE_TOKEN`: authentication token for the user sending
|
129
149
|
build notifications.
|
130
|
-
|
131
|
-
|
150
|
+
|
151
|
+
[Campfire]: http://campfirenow.com/
|
152
|
+
|
153
|
+
#### HipChat
|
154
|
+
|
155
|
+
Required settings:
|
156
|
+
|
157
|
+
* `JANKY_CHAT=hipchat`
|
158
|
+
* `JANKY_CHAT_HIPCHAT_TOKEN`: authentication token
|
159
|
+
* `JANKY_CHAT_HIPCHAT_FROM`: name that messages will appear be sent from.
|
160
|
+
Defaults to `CI`.
|
161
|
+
|
162
|
+
Installation:
|
163
|
+
|
164
|
+
* Add `require "janky/chat_service/hipchat"` to the `config.ru` file.
|
165
|
+
* `echo 'gem "hipchat", "~>0.4" >> Gemfile'`
|
166
|
+
* `bundle`
|
167
|
+
* `git commit -am "install hipchat"`
|
168
|
+
|
169
|
+
### Authentication
|
132
170
|
|
133
171
|
To restrict access to members of a GitHub organization, [register a new
|
134
172
|
OAuth application on GitHub](https://github.com/account/applications)
|
@@ -143,10 +181,10 @@ a few extra settings:
|
|
143
181
|
|
144
182
|
### Hubot
|
145
183
|
|
146
|
-
Install the [
|
184
|
+
Install the [janky script](http://git.io/hubot-janky) in your Hubot
|
147
185
|
then set the `HUBOT_JANKY_URL` environment variable. Example:
|
148
186
|
`http://user:secret@janky.example.com/_hubot/`, with user and password
|
149
|
-
replaced by `JANKY_HUBOT_USER` and `JANKY_HUBOT_PASSWORD`
|
187
|
+
replaced by `JANKY_HUBOT_USER` and `JANKY_HUBOT_PASSWORD` respectively.
|
150
188
|
|
151
189
|
### Custom Build Configuration
|
152
190
|
|
@@ -168,6 +206,10 @@ server.
|
|
168
206
|
Hacking
|
169
207
|
-------
|
170
208
|
|
209
|
+
Get your environment up and running:
|
210
|
+
|
211
|
+
$ script/bootstrap
|
212
|
+
|
171
213
|
Create the databases:
|
172
214
|
|
173
215
|
$ mysqladmin -uroot create janky_development
|
@@ -178,10 +220,6 @@ Create the tables:
|
|
178
220
|
$ RACK_ENV=development bin/rake db:migrate
|
179
221
|
$ RACK_ENV=test bin/rake db:migrate
|
180
222
|
|
181
|
-
Get your environment up and running:
|
182
|
-
|
183
|
-
$ script/boostrap
|
184
|
-
|
185
223
|
Seed some data into the development database:
|
186
224
|
|
187
225
|
$ bin/rake db:seed
|
@@ -196,7 +234,7 @@ Open the app:
|
|
196
234
|
|
197
235
|
Run the test suite:
|
198
236
|
|
199
|
-
$
|
237
|
+
$ bin/rake
|
200
238
|
|
201
239
|
Contributing
|
202
240
|
------------
|
@@ -207,5 +245,5 @@ send a Pull Request.
|
|
207
245
|
Copying
|
208
246
|
-------
|
209
247
|
|
210
|
-
Copyright © 2011, GitHub, Inc. See the `COPYING` file for license
|
248
|
+
Copyright © 2011-2012, GitHub, Inc. See the `COPYING` file for license
|
211
249
|
rights and limitations (MIT).
|
data/Rakefile
CHANGED
data/janky.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new "janky", Janky::VERSION do |s|
|
|
13
13
|
s.add_dependency "sinatra", "~>1.3"
|
14
14
|
s.add_dependency "sinatra_auth_github", "~>0.1.5"
|
15
15
|
s.add_dependency "mustache", "~>0.11"
|
16
|
-
s.add_dependency "yajl-ruby", "~>0
|
16
|
+
s.add_dependency "yajl-ruby", "~>1.1.0"
|
17
17
|
s.add_dependency "activerecord", "~>3.1.0"
|
18
18
|
s.add_dependency "broach", "~>0.2"
|
19
19
|
s.add_dependency "replicate", "~>1.4"
|
@@ -46,8 +46,10 @@ lib/janky/builder/mock.rb
|
|
46
46
|
lib/janky/builder/payload.rb
|
47
47
|
lib/janky/builder/receiver.rb
|
48
48
|
lib/janky/builder/runner.rb
|
49
|
-
lib/janky/
|
50
|
-
lib/janky/campfire
|
49
|
+
lib/janky/chat_service.rb
|
50
|
+
lib/janky/chat_service/campfire.rb
|
51
|
+
lib/janky/chat_service/hipchat.rb
|
52
|
+
lib/janky/chat_service/mock.rb
|
51
53
|
lib/janky/commit.rb
|
52
54
|
lib/janky/database/migrate/1312115512_init.rb
|
53
55
|
lib/janky/database/migrate/1312117285_non_unique_repo_uri.rb
|
@@ -72,7 +74,7 @@ lib/janky/helpers.rb
|
|
72
74
|
lib/janky/hubot.rb
|
73
75
|
lib/janky/job_creator.rb
|
74
76
|
lib/janky/notifier.rb
|
75
|
-
lib/janky/notifier/
|
77
|
+
lib/janky/notifier/chat_service.rb
|
76
78
|
lib/janky/notifier/mock.rb
|
77
79
|
lib/janky/notifier/multi.rb
|
78
80
|
lib/janky/public/css/base.css
|
data/lib/janky.rb
CHANGED
@@ -33,12 +33,14 @@ require "janky/builder/http"
|
|
33
33
|
require "janky/builder/mock"
|
34
34
|
require "janky/builder/payload"
|
35
35
|
require "janky/builder/receiver"
|
36
|
-
require "janky/
|
36
|
+
require "janky/chat_service"
|
37
|
+
require "janky/chat_service/campfire"
|
38
|
+
require "janky/chat_service/mock"
|
37
39
|
require "janky/exception"
|
38
40
|
require "janky/notifier"
|
41
|
+
require "janky/notifier/chat_service"
|
39
42
|
require "janky/notifier/mock"
|
40
43
|
require "janky/notifier/multi"
|
41
|
-
require "janky/notifier/campfire"
|
42
44
|
require "janky/app"
|
43
45
|
require "janky/views/layout"
|
44
46
|
require "janky/views/index"
|
@@ -53,9 +55,9 @@ module Janky
|
|
53
55
|
|
54
56
|
# Setup the application, including the database and Jenkins connections.
|
55
57
|
#
|
56
|
-
# settings - Hash of app settings. Typically ENV but any object
|
57
|
-
# to #[] is valid. See required_settings for
|
58
|
-
# The RACK_ENV
|
58
|
+
# settings - Hash of app settings. Typically ENV but any object that responds
|
59
|
+
# to #[], #[]= and #each is valid. See required_settings for
|
60
|
+
# required keys. The RACK_ENV key is always required.
|
59
61
|
#
|
60
62
|
# Raises an Error when required settings are missing.
|
61
63
|
# Returns nothing.
|
@@ -75,13 +77,20 @@ module Janky
|
|
75
77
|
|
76
78
|
if env != "production"
|
77
79
|
settings["DATABASE_URL"] ||= "mysql2://root@localhost/janky_#{env}"
|
78
|
-
settings["JANKY_BASE_URL"] ||= "http://localhost:9393"
|
80
|
+
settings["JANKY_BASE_URL"] ||= "http://localhost:9393/"
|
79
81
|
settings["JANKY_BUILDER_DEFAULT"] ||= "http://localhost:8080/"
|
80
82
|
settings["JANKY_CONFIG_DIR"] ||= File.dirname(__FILE__)
|
83
|
+
settings["JANKY_CHAT"] = "campfire"
|
84
|
+
settings["JANKY_CHAT_CAMPFIRE_ACCOUNT"] = "account"
|
85
|
+
settings["JANKY_CHAT_CAMPFIRE_TOKEN"] = "token"
|
81
86
|
end
|
82
87
|
|
83
88
|
database = URI(settings["DATABASE_URL"])
|
84
89
|
adapter = database.scheme == "postgres" ? "postgresql" : database.scheme
|
90
|
+
if settings["JANKY_BASE_URL"][-1] != ?/
|
91
|
+
warn "JANKY_BASE_URL must have a trailing slash"
|
92
|
+
settings["JANKY_BASE_URL"] = settings["JANKY_BASE_URL"] + "/"
|
93
|
+
end
|
85
94
|
base_url = URI(settings["JANKY_BASE_URL"]).to_s
|
86
95
|
|
87
96
|
ActiveRecord::Base.establish_connection(
|
@@ -99,16 +108,32 @@ module Janky
|
|
99
108
|
end
|
100
109
|
|
101
110
|
# Setup the callback URL of this Janky host.
|
102
|
-
Janky::Builder.setup(base_url + "
|
111
|
+
Janky::Builder.setup(base_url + "_builder")
|
103
112
|
|
104
113
|
# Setup the default Jenkins build host
|
114
|
+
if settings["JANKY_BUILDER_DEFAULT"][-1] != ?/
|
115
|
+
raise Error, "JANKY_BUILDER_DEFAULT must have a trailing slash"
|
116
|
+
end
|
105
117
|
Janky::Builder[:default] = settings["JANKY_BUILDER_DEFAULT"]
|
106
118
|
|
119
|
+
if settings.key?("JANKY_GITHUB_API_URL")
|
120
|
+
api_url = settings["JANKY_GITHUB_API_URL"]
|
121
|
+
git_host = URI(api_url).host
|
122
|
+
else
|
123
|
+
api_url = "https://api.github.com/"
|
124
|
+
git_host = "github.com"
|
125
|
+
end
|
126
|
+
if api_url[-1] != ?/
|
127
|
+
raise Error, "JANKY_GITHUB_API_URL must have a trailing slash"
|
128
|
+
end
|
129
|
+
hook_url = base_url + "_github"
|
107
130
|
Janky::GitHub.setup(
|
108
131
|
settings["JANKY_GITHUB_USER"],
|
109
132
|
settings["JANKY_GITHUB_PASSWORD"],
|
110
133
|
settings["JANKY_GITHUB_HOOK_SECRET"],
|
111
|
-
|
134
|
+
hook_url,
|
135
|
+
api_url,
|
136
|
+
git_host
|
112
137
|
)
|
113
138
|
|
114
139
|
if settings.key?("JANKY_SESSION_SECRET")
|
@@ -132,15 +157,38 @@ module Janky
|
|
132
157
|
:password => settings["JANKY_HUBOT_PASSWORD"]
|
133
158
|
)
|
134
159
|
|
135
|
-
Janky::
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
160
|
+
Janky::Exception.setup(Janky::Exception::Logger.new($stderr))
|
161
|
+
|
162
|
+
if campfire_account = settings["JANKY_CAMPFIRE_ACCOUNT"]
|
163
|
+
warn "JANKY_CAMPFIRE_ACCOUNT is deprecated. Please use " \
|
164
|
+
"JANKY_CHAT_CAMPFIRE_ACCOUNT instead."
|
165
|
+
settings["JANKY_CHAT_CAMPFIRE_ACCOUNT"] ||=
|
166
|
+
settings["JANKY_CAMPFIRE_ACCOUNT"]
|
167
|
+
end
|
168
|
+
|
169
|
+
if campfire_token = settings["JANKY_CAMPFIRE_TOKEN"]
|
170
|
+
warn "JANKY_CAMPFIRE_TOKEN is deprecated. Please use " \
|
171
|
+
"JANKY_CHAT_CAMPFIRE_TOKEN instead."
|
172
|
+
settings["JANKY_CHAT_CAMPFIRE_TOKEN"] ||=
|
173
|
+
settings["JANKY_CAMPFIRE_ACCOUNT"]
|
174
|
+
end
|
140
175
|
|
141
|
-
|
176
|
+
chat_name = settings["JANKY_CHAT"] || "campfire"
|
177
|
+
chat_settings = {}
|
178
|
+
settings.each do |key, value|
|
179
|
+
if key =~ /^JANKY_CHAT_#{chat_name.upcase}_/
|
180
|
+
chat_settings[key] = value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
chat_room = settings["JANKY_CHAT_DEFAULT_ROOM"] ||
|
184
|
+
settings["JANKY_CAMPFIRE_DEFAULT_ROOM"]
|
185
|
+
if settings["JANKY_CAMPFIRE_DEFAULT_ROOM"]
|
186
|
+
warn "JANKY_CAMPFIRE_DEFAULT_ROOM is deprecated. Please use " \
|
187
|
+
"JANKY_CHAT_DEFAULT_ROOM instead."
|
188
|
+
end
|
189
|
+
ChatService.setup(chat_name, chat_settings, chat_room)
|
142
190
|
|
143
|
-
Notifier.setup(Notifier::
|
191
|
+
Notifier.setup(Notifier::ChatService)
|
144
192
|
end
|
145
193
|
|
146
194
|
# List of settings required in production.
|
@@ -152,8 +200,7 @@ module Janky
|
|
152
200
|
JANKY_BUILDER_DEFAULT
|
153
201
|
JANKY_CONFIG_DIR
|
154
202
|
JANKY_GITHUB_USER JANKY_GITHUB_PASSWORD JANKY_GITHUB_HOOK_SECRET
|
155
|
-
JANKY_HUBOT_USER JANKY_HUBOT_PASSWORD
|
156
|
-
JANKY_CAMPFIRE_ACCOUNT JANKY_CAMPFIRE_TOKEN JANKY_CAMPFIRE_DEFAULT_ROOM]
|
203
|
+
JANKY_HUBOT_USER JANKY_HUBOT_PASSWORD]
|
157
204
|
end
|
158
205
|
|
159
206
|
# Directory where Jenkins job configuration templates are located.
|
@@ -171,7 +218,7 @@ module Janky
|
|
171
218
|
Janky::Builder.enable_mock!
|
172
219
|
Janky::GitHub.enable_mock!
|
173
220
|
Janky::Notifier.enable_mock!
|
174
|
-
Janky::
|
221
|
+
Janky::ChatService.enable_mock!
|
175
222
|
Janky::App.disable :github_team_id
|
176
223
|
end
|
177
224
|
|
@@ -183,10 +230,10 @@ module Janky
|
|
183
230
|
Janky::Builder.reset!
|
184
231
|
end
|
185
232
|
|
186
|
-
# The Janky Rack application, assembled from four apps. Exceptions
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
233
|
+
# The Janky Rack application, assembled from four apps. Exceptions raised
|
234
|
+
# during the request cycle are caught by the Exception middleware which
|
235
|
+
# typically reports them to an external service before re-raising the
|
236
|
+
# exception.
|
190
237
|
#
|
191
238
|
# Returns a memoized Rack application.
|
192
239
|
def self.app
|
@@ -221,4 +268,16 @@ module Janky
|
|
221
268
|
end
|
222
269
|
}
|
223
270
|
end
|
271
|
+
|
272
|
+
# Register a Chat service implementation.
|
273
|
+
#
|
274
|
+
# name - Service name as a String, e.g. "irc".
|
275
|
+
# service - Constant for the implementation.
|
276
|
+
#
|
277
|
+
# Returns nothing.
|
278
|
+
def self.register_chat_service(name, service)
|
279
|
+
Janky::ChatService.adapters[name] = service
|
280
|
+
end
|
281
|
+
|
282
|
+
register_chat_service "campfire", ChatService::Campfire
|
224
283
|
end
|
data/lib/janky/app.rb
CHANGED
data/lib/janky/build.rb
CHANGED
@@ -180,7 +180,7 @@ module Janky
|
|
180
180
|
# Returns the String room name.
|
181
181
|
def room_name
|
182
182
|
if room_id && room_id > 0
|
183
|
-
|
183
|
+
ChatService.room_name(room_id)
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
@@ -212,6 +212,14 @@ module Janky
|
|
212
212
|
commit.url
|
213
213
|
end
|
214
214
|
|
215
|
+
def commit_message
|
216
|
+
commit.message
|
217
|
+
end
|
218
|
+
|
219
|
+
def commit_author
|
220
|
+
commit.author
|
221
|
+
end
|
222
|
+
|
215
223
|
def number
|
216
224
|
id.to_s
|
217
225
|
end
|