cloudist 0.2.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Gemfile +15 -11
  2. data/Gemfile.lock +20 -7
  3. data/README.md +61 -39
  4. data/VERSION +1 -1
  5. data/cloudist.gemspec +50 -16
  6. data/examples/amqp/Gemfile +3 -0
  7. data/examples/amqp/Gemfile.lock +12 -0
  8. data/examples/amqp/amqp_consumer.rb +56 -0
  9. data/examples/amqp/amqp_publisher.rb +50 -0
  10. data/examples/queue_message.rb +7 -7
  11. data/examples/sandwich_client_with_custom_listener.rb +77 -0
  12. data/examples/sandwich_worker_with_class.rb +22 -7
  13. data/lib/cloudist.rb +113 -56
  14. data/lib/cloudist/application.rb +60 -0
  15. data/lib/cloudist/core_ext/class.rb +139 -0
  16. data/lib/cloudist/core_ext/kernel.rb +13 -0
  17. data/lib/cloudist/core_ext/module.rb +11 -0
  18. data/lib/cloudist/encoding.rb +13 -0
  19. data/lib/cloudist/errors.rb +2 -0
  20. data/lib/cloudist/job.rb +21 -18
  21. data/lib/cloudist/listener.rb +108 -54
  22. data/lib/cloudist/message.rb +97 -0
  23. data/lib/cloudist/messaging.rb +29 -0
  24. data/lib/cloudist/payload.rb +45 -105
  25. data/lib/cloudist/payload_old.rb +155 -0
  26. data/lib/cloudist/publisher.rb +7 -2
  27. data/lib/cloudist/queue.rb +152 -0
  28. data/lib/cloudist/queues/basic_queue.rb +83 -53
  29. data/lib/cloudist/queues/job_queue.rb +13 -24
  30. data/lib/cloudist/queues/reply_queue.rb +13 -21
  31. data/lib/cloudist/request.rb +33 -7
  32. data/lib/cloudist/worker.rb +9 -2
  33. data/lib/cloudist_old.rb +300 -0
  34. data/lib/em/em_timer_utils.rb +55 -0
  35. data/lib/em/iterator.rb +27 -0
  36. data/spec/cloudist/message_spec.rb +91 -0
  37. data/spec/cloudist/messaging_spec.rb +19 -0
  38. data/spec/cloudist/payload_spec.rb +10 -4
  39. data/spec/cloudist/payload_spec_2_spec.rb +78 -0
  40. data/spec/cloudist/queue_spec.rb +16 -0
  41. data/spec/cloudist_spec.rb +49 -45
  42. data/spec/spec_helper.rb +0 -1
  43. data/spec/support/amqp.rb +16 -0
  44. metadata +112 -102
  45. data/examples/extending_values.rb +0 -44
  46. data/examples/sandwich_client.rb +0 -57
  47. data/lib/cloudist/callback.rb +0 -16
  48. data/lib/cloudist/callback_methods.rb +0 -19
  49. data/lib/cloudist/callbacks/error_callback.rb +0 -14
data/Gemfile CHANGED
@@ -1,18 +1,22 @@
1
- source "http://rubygems.org"
1
+ source :rubygems
2
2
 
3
- gem "amqp", "~> 0.6.7"
4
- gem "json", "~> 1.4.6"
5
- gem "activesupport", "~> 3.0.3"
3
+ gem 'amqp', '~>0.8.0.rc12'
4
+ gem "json", "~> 1.4.6"
5
+ gem "i18n"
6
+ gem "activesupport", "~> 3.0.3"
7
+ gem "hashie"
8
+ gem "uuid"
6
9
 
7
10
  # Add dependencies to develop your gem here.
8
11
  # Include everything needed to run rake, tests, features, etc.
9
12
  group :development do
10
- gem "rspec", "~> 2.3.0"
11
- gem "moqueue", :git => "git://github.com/ivanvanderbyl/moqueue.git"
13
+ gem "rake", "~> 0.8.7"
14
+ gem "rspec", "~> 2.3.0"
15
+ gem "moqueue", :git => "git://github.com/ivanvanderbyl/moqueue.git"
12
16
  gem "mocha"
13
- gem "bundler", "~> 1.0.0"
14
- gem "jeweler", "~> 1.5.2"
15
- gem "rcov", ">= 0"
16
- gem "reek", "~> 1.2.8"
17
- gem "roodi", "~> 2.1.0"
17
+ gem "bundler", "~> 1.0.0"
18
+ gem "jeweler", "~> 1.5.2"
19
+ gem "rcov", ">= 0"
20
+ gem "reek", "~> 1.2.8"
21
+ gem "roodi", "~> 2.1.0"
18
22
  end
@@ -8,19 +8,26 @@ GIT
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- activesupport (3.0.3)
12
- amqp (0.6.7)
13
- eventmachine (>= 0.12.4)
11
+ activesupport (3.0.7)
12
+ amq-client (0.7.0.alpha25)
13
+ amq-protocol
14
+ eventmachine
15
+ amq-protocol (0.5.0)
16
+ amqp (0.8.0.rc12)
17
+ amq-client (>= 0.7.0.alpha25)
18
+ eventmachine
14
19
  diff-lcs (1.1.2)
15
20
  eventmachine (0.12.10)
16
21
  git (1.2.5)
22
+ hashie (1.0.0)
23
+ i18n (0.5.0)
17
24
  jeweler (1.5.2)
18
25
  bundler (~> 1.0.0)
19
26
  git (>= 1.2.5)
20
27
  rake
21
28
  json (1.4.6)
22
- mocha (0.9.10)
23
- rake
29
+ macaddr (1.0.0)
30
+ mocha (0.9.12)
24
31
  rake (0.8.7)
25
32
  rcov (0.9.9)
26
33
  reek (1.2.8)
@@ -40,22 +47,28 @@ GEM
40
47
  ruby2ruby (1.2.5)
41
48
  ruby_parser (~> 2.0)
42
49
  sexp_processor (~> 3.0)
43
- ruby_parser (2.0.5)
50
+ ruby_parser (2.0.6)
44
51
  sexp_processor (~> 3.0)
45
52
  sexp_processor (3.0.5)
53
+ uuid (2.3.2)
54
+ macaddr (~> 1.0)
46
55
 
47
56
  PLATFORMS
48
57
  ruby
49
58
 
50
59
  DEPENDENCIES
51
60
  activesupport (~> 3.0.3)
52
- amqp (~> 0.6.7)
61
+ amqp (~> 0.8.0.rc12)
53
62
  bundler (~> 1.0.0)
63
+ hashie
64
+ i18n
54
65
  jeweler (~> 1.5.2)
55
66
  json (~> 1.4.6)
56
67
  mocha
57
68
  moqueue!
69
+ rake (~> 0.8.7)
58
70
  rcov
59
71
  reek (~> 1.2.8)
60
72
  roodi (~> 2.1.0)
61
73
  rspec (~> 2.3.0)
74
+ uuid
data/README.md CHANGED
@@ -1,16 +1,12 @@
1
+ ![Cloudist](https://github.com/ivanvanderbyl/cloudist/raw/master/doc/cloudist.png)
2
+
1
3
  Cloudist
2
4
  ========
3
5
 
4
- Cloudist is a super fast job queue for high demand and scalable tasks. It uses AMQP (RabbitMQ mainly) for message store are
5
- distribution, while providing a simple DSL for handling jobs and responses.
6
-
7
- Cloudist can be used within Rails or just about any Ruby app to distribute long running tasks, such as encoding a video, generating PDFs, scraping site data
8
- or even just sending emails. Unlike other job queues (DelayedJob etc) Cloudist does not load your entire Rails stack into memory for every worker, and it is not designed to, instead it
9
- expects all the data your worker requires to be sent in the initial job request. This means your workers stay slim and can scale very quickly and even run on EC2 micros outside your applications
10
- network without any further configuration.
6
+ Cloudist is a simple, highly scalable job queue for Ruby applications, it can run within Rails, DaemonKit or your own custom application. Cloudist uses AMQP (RabbitMQ mainly) for transport and provides a simple DSL for queuing jobs and receiving responses including logs, exceptions and job progress.
11
7
 
12
- Another way Cloudist differs from other AMQP based job queues like Minion is it allows workers to report events, logs, system stats and replies back to the application which distributed the
13
- job, and unlike database based job queues, there is almost no delay between messages, except network latency of course.
8
+ Cloudist can be used to distribute long running tasks such as encoding a video, generating PDFs, scraping site data
9
+ or even just sending emails. Unlike other job queues (DelayedJob etc) Cloudist does not load your entire Rails stack into memory for every worker, and it is not designed to, instead it expects all the data your worker requires to complete a job to be sent in the initial job request. This means your workers stay slim and can scale very quickly and even run on EC2 micros outside your applications environment without any further configuration.
14
10
 
15
11
  Installation
16
12
  ------------
@@ -24,74 +20,95 @@ Or if your app has a Gemfile:
24
20
  Usage
25
21
  -----
26
22
 
27
- Cloudist requires an EventMachine reactor loop and an AMQP connection, so if your application is already using one, or your web server supplies one (for example Thin) these examples will work
28
- out of the box. Otherwise simply wrap everything inside this block:
23
+ Cloudist requires an EventMachine reactor loop and an AMQP connection, so if your application is already using one, or your web server supplies one (for example Thin) these examples will work out of the box. Otherwise simply wrap everything inside this block:
29
24
 
30
- Cloudist.settings = {:user => 'guest'} # Standard AMQP settings
31
25
  Cloudist.start {
32
26
  # usual stuff here
33
- worker {
34
- # define a worker
27
+ job('make.sandwich') {
28
+ # define a job handler
35
29
  }
36
30
  }
37
31
 
38
- This will start and AMQP connection and EM loop then yield everything inside it.
32
+ This will start an AMQP connection and EM loop then yield everything inside it.
39
33
 
40
34
  In your worker:
41
35
 
42
36
  Cloudist.start {
43
37
  log.info("Started Worker")
44
38
 
45
- worker {
46
- job('make.sandwich') {
47
- log.info("JOB (#{id}) Make sandwich with #{data[:bread]} bread")
48
- log.debug(data.inspect)
49
-
50
- # Do long running tasks here
51
- EM.defer {
52
- progress(0)
53
- started!
54
- progress(10)
55
- sleep(1)
56
- progress(20)
57
- sleep(5)
58
- progress(90)
59
- sleep(1)
60
- finished!
61
- progress(100)
62
- }
63
- }
39
+ job('make.sandwich') {
40
+ log.info("JOB (#{id}) Make sandwich with #{data[:bread]} bread")
41
+
42
+ job.started!
43
+
44
+ (1..20).each do |i|
45
+ job.progress(i * 5)
46
+ sleep(1)
47
+ end
48
+ job.finished!
64
49
  }
50
+
65
51
  }
66
52
 
67
53
  In your application:
68
54
 
69
55
  Cloudist.start {
70
- # Enqueue sandwich job
56
+
71
57
  log.info("Dispatching sandwich making job...")
72
- enqueue('make.sandwich', {:bread => 'white'})
58
+
59
+ Cloudist.enqueue('make.sandwich', {:bread => 'white', :sandwich_number => 1})
73
60
 
74
61
  # Listen to all sandwich jobs
75
62
  listen('make.sandwich') {
63
+ everything {
64
+ Cloudist.log.info("#{headers[:message_type]} - Job ID: #{job_id}")
65
+ }
66
+
67
+ # This will contain any exceptions which are raised while processing the job, which will halt the job
68
+ error { |e|
69
+ Cloudist.log.error(e.inspect)
70
+ Cloudist.log.error(e.backtrace.inspect)
71
+
72
+ # Exit on failure
73
+ Cloudist.stop
74
+ }
75
+
76
+ # Process progress updates
76
77
  progress {
77
78
  Cloudist.log.info("Progress: #{data[:progress]}")
78
79
  }
79
-
80
+
80
81
  event('started') {
81
82
  Cloudist.log.info("Started making sandwich at #{Time.now.to_s}")
82
83
  }
83
84
 
84
85
  event('finished'){
85
86
  Cloudist.log.info("Finished making sandwich at #{Time.now.to_s}")
87
+ # Exit when done
88
+ Cloudist.stop
86
89
  }
87
90
  }
88
91
 
89
92
  }
93
+
90
94
 
91
95
  If your application provides an AMQP.start loop already, you can skip the Cloudist.start
92
96
 
97
+ Configuration
98
+ -------------
99
+
100
+ The only configuration required to get going are the AMQP settings, these can be set in two ways:
101
+
102
+ 1. Using the `AMQP_URL` environment variable with value of `amqp://username:password@localhost:5672/vhost`
103
+
104
+ 2. Updating the settings hash manually:
105
+
106
+
107
+ Cloudist.settings = {:user => 'guest', :pass => 'password', :vhost => '/', :host => 'localhost', :port => 5672}
108
+
109
+
93
110
  Acknowledgements
94
- -------
111
+ ----------------
95
112
 
96
113
  Portions of this gem are based on code from the following projects:
97
114
 
@@ -109,7 +126,12 @@ Contributing to Cloudist
109
126
  * Commit and push until you are happy with your contribution
110
127
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
111
128
  * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
112
-
129
+
130
+ Authors
131
+ -------
132
+
133
+ Ivan Vanderbyl - [@IvanVanderbyl](http://twitter.com/IvanVanderbyl) - [Blog](http://ivanvanderbyl.github.com/)
134
+
113
135
  Copyright
114
136
  ---------
115
137
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.4.1
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cloudist}
8
- s.version = "0.2.1"
8
+ s.version = "0.4.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ivan Vanderbyl"]
12
- s.date = %q{2011-01-20}
12
+ s.date = %q{2011-05-23}
13
13
  s.description = %q{Cloudist is a simple, highly scalable job queue for Ruby applications, it can run within Rails, DaemonKit or your own custom application. Refer to github page for examples}
14
14
  s.email = %q{ivanvanderbyl@me.com}
15
15
  s.extra_rdoc_files = [
@@ -26,66 +26,92 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "cloudist.gemspec",
29
- "examples/extending_values.rb",
29
+ "examples/amqp/Gemfile",
30
+ "examples/amqp/Gemfile.lock",
31
+ "examples/amqp/amqp_consumer.rb",
32
+ "examples/amqp/amqp_publisher.rb",
30
33
  "examples/queue_message.rb",
31
- "examples/sandwich_client.rb",
34
+ "examples/sandwich_client_with_custom_listener.rb",
32
35
  "examples/sandwich_worker.rb",
33
36
  "examples/sandwich_worker_with_class.rb",
34
37
  "lib/cloudist.rb",
35
- "lib/cloudist/callback.rb",
36
- "lib/cloudist/callback_methods.rb",
37
- "lib/cloudist/callbacks/error_callback.rb",
38
+ "lib/cloudist/application.rb",
39
+ "lib/cloudist/core_ext/class.rb",
40
+ "lib/cloudist/core_ext/kernel.rb",
41
+ "lib/cloudist/core_ext/module.rb",
38
42
  "lib/cloudist/core_ext/object.rb",
39
43
  "lib/cloudist/core_ext/string.rb",
44
+ "lib/cloudist/encoding.rb",
40
45
  "lib/cloudist/errors.rb",
41
46
  "lib/cloudist/job.rb",
42
47
  "lib/cloudist/listener.rb",
48
+ "lib/cloudist/message.rb",
49
+ "lib/cloudist/messaging.rb",
43
50
  "lib/cloudist/payload.rb",
51
+ "lib/cloudist/payload_old.rb",
44
52
  "lib/cloudist/publisher.rb",
53
+ "lib/cloudist/queue.rb",
45
54
  "lib/cloudist/queues/basic_queue.rb",
46
55
  "lib/cloudist/queues/job_queue.rb",
47
56
  "lib/cloudist/queues/reply_queue.rb",
48
57
  "lib/cloudist/request.rb",
49
58
  "lib/cloudist/utils.rb",
50
59
  "lib/cloudist/worker.rb",
60
+ "lib/cloudist_old.rb",
61
+ "lib/em/em_timer_utils.rb",
62
+ "lib/em/iterator.rb",
51
63
  "spec/cloudist/basic_queue_spec.rb",
52
64
  "spec/cloudist/job_spec.rb",
65
+ "spec/cloudist/message_spec.rb",
66
+ "spec/cloudist/messaging_spec.rb",
53
67
  "spec/cloudist/payload_spec.rb",
68
+ "spec/cloudist/payload_spec_2_spec.rb",
69
+ "spec/cloudist/queue_spec.rb",
54
70
  "spec/cloudist/request_spec.rb",
55
71
  "spec/cloudist/utils_spec.rb",
56
72
  "spec/cloudist_spec.rb",
57
73
  "spec/core_ext/string_spec.rb",
58
- "spec/spec_helper.rb"
74
+ "spec/spec_helper.rb",
75
+ "spec/support/amqp.rb"
59
76
  ]
60
77
  s.homepage = %q{http://github.com/ivanvanderbyl/cloudist}
61
78
  s.licenses = ["MIT"]
62
79
  s.require_paths = ["lib"]
63
- s.rubygems_version = %q{1.3.7}
80
+ s.rubygems_version = %q{1.6.2}
64
81
  s.summary = %q{Super fast job queue using AMQP}
65
82
  s.test_files = [
66
- "examples/extending_values.rb",
83
+ "examples/amqp/amqp_consumer.rb",
84
+ "examples/amqp/amqp_publisher.rb",
67
85
  "examples/queue_message.rb",
68
- "examples/sandwich_client.rb",
86
+ "examples/sandwich_client_with_custom_listener.rb",
69
87
  "examples/sandwich_worker.rb",
70
88
  "examples/sandwich_worker_with_class.rb",
71
89
  "spec/cloudist/basic_queue_spec.rb",
72
90
  "spec/cloudist/job_spec.rb",
91
+ "spec/cloudist/message_spec.rb",
92
+ "spec/cloudist/messaging_spec.rb",
73
93
  "spec/cloudist/payload_spec.rb",
94
+ "spec/cloudist/payload_spec_2_spec.rb",
95
+ "spec/cloudist/queue_spec.rb",
74
96
  "spec/cloudist/request_spec.rb",
75
97
  "spec/cloudist/utils_spec.rb",
76
98
  "spec/cloudist_spec.rb",
77
99
  "spec/core_ext/string_spec.rb",
78
- "spec/spec_helper.rb"
100
+ "spec/spec_helper.rb",
101
+ "spec/support/amqp.rb"
79
102
  ]
80
103
 
81
104
  if s.respond_to? :specification_version then
82
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
83
105
  s.specification_version = 3
84
106
 
85
107
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
86
- s.add_runtime_dependency(%q<amqp>, ["~> 0.6.7"])
108
+ s.add_runtime_dependency(%q<amqp>, ["~> 0.8.0.rc12"])
87
109
  s.add_runtime_dependency(%q<json>, ["~> 1.4.6"])
110
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
88
111
  s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.3"])
112
+ s.add_runtime_dependency(%q<hashie>, [">= 0"])
113
+ s.add_runtime_dependency(%q<uuid>, [">= 0"])
114
+ s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
89
115
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
90
116
  s.add_development_dependency(%q<moqueue>, [">= 0"])
91
117
  s.add_development_dependency(%q<mocha>, [">= 0"])
@@ -95,9 +121,13 @@ Gem::Specification.new do |s|
95
121
  s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
96
122
  s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
97
123
  else
98
- s.add_dependency(%q<amqp>, ["~> 0.6.7"])
124
+ s.add_dependency(%q<amqp>, ["~> 0.8.0.rc12"])
99
125
  s.add_dependency(%q<json>, ["~> 1.4.6"])
126
+ s.add_dependency(%q<i18n>, [">= 0"])
100
127
  s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
128
+ s.add_dependency(%q<hashie>, [">= 0"])
129
+ s.add_dependency(%q<uuid>, [">= 0"])
130
+ s.add_dependency(%q<rake>, ["~> 0.8.7"])
101
131
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
102
132
  s.add_dependency(%q<moqueue>, [">= 0"])
103
133
  s.add_dependency(%q<mocha>, [">= 0"])
@@ -108,9 +138,13 @@ Gem::Specification.new do |s|
108
138
  s.add_dependency(%q<roodi>, ["~> 2.1.0"])
109
139
  end
110
140
  else
111
- s.add_dependency(%q<amqp>, ["~> 0.6.7"])
141
+ s.add_dependency(%q<amqp>, ["~> 0.8.0.rc12"])
112
142
  s.add_dependency(%q<json>, ["~> 1.4.6"])
143
+ s.add_dependency(%q<i18n>, [">= 0"])
113
144
  s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
145
+ s.add_dependency(%q<hashie>, [">= 0"])
146
+ s.add_dependency(%q<uuid>, [">= 0"])
147
+ s.add_dependency(%q<rake>, ["~> 0.8.7"])
114
148
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
115
149
  s.add_dependency(%q<moqueue>, [">= 0"])
116
150
  s.add_dependency(%q<mocha>, [">= 0"])
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gem 'amqp', '~>0.7'
@@ -0,0 +1,12 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ amqp (0.7.1)
5
+ eventmachine (>= 0.12.4)
6
+ eventmachine (0.12.10)
7
+
8
+ PLATFORMS
9
+ ruby
10
+
11
+ DEPENDENCIES
12
+ amqp (~> 0.7)
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "rubygems"
5
+ require 'amqp'
6
+
7
+ def amqp_settings
8
+ uri = URI.parse(ENV["AMQP_URL"] || 'amqp://guest:guest@localhost:5672/')
9
+ {
10
+ :vhost => uri.path,
11
+ :host => uri.host,
12
+ :user => uri.user,
13
+ :port => uri.port || 5672,
14
+ :pass => uri.password,
15
+ :heartbeat => 120,
16
+ :logging => false
17
+ }
18
+ rescue Object => e
19
+ raise "invalid AMQP_URL: (#{uri.inspect}) #{e.class} -> #{e.message}"
20
+ end
21
+
22
+ p amqp_settings
23
+
24
+ def log(*args)
25
+ puts args.inspect
26
+ end
27
+
28
+ EM.run do
29
+ puts "Running..."
30
+ AMQP.start(amqp_settings) do |connection|
31
+ log "Connected to AMQP broker"
32
+
33
+ channel = AMQP::Channel.new(connection)
34
+ channel.prefetch(1)
35
+ queue = channel.queue("test.hello.world")
36
+ exchange = channel.direct
37
+ queue.bind(exchange)
38
+
39
+ @count = 0
40
+
41
+ queue.subscribe(:ack => true) do |h, payload|
42
+ puts "--"
43
+ EM.defer {
44
+ # sleep(1)
45
+ @count += 1
46
+ log "Received a message: #{payload} - #{@count}"
47
+ h.ack
48
+ }
49
+ end
50
+
51
+ # queue.subscribe(:ack => false) do |h, payload|
52
+ # @count += 1
53
+ # log "Received a message: #{payload} - #{@count}"
54
+ # end
55
+ end
56
+ end