evenitron 0.1.0 → 0.2.0

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.
@@ -1,23 +1,28 @@
1
- # Setting up with delayed_job
1
+ # Evenitron
2
2
 
3
- There are two options for setting up Evenitron to monitor your delayed_job tasks.
3
+ Evenitron is a service to help you to properly understand the performance of your delayed_job and resque jobs.
4
+
5
+ ## Getting started
6
+
7
+ In order to use the Evenitron service you will need a `system key` which is assigned to you when you sign up for an account. This key identifies you system to the Evenitron aggregators.
8
+
9
+ If you don't have an account you can request an invite to our private beta program here : http://app.evenitron.com/invite_requests/new
4
10
 
5
11
  Firstly, add the gem to your Gemfile:
6
12
 
7
13
  ```ruby
8
- gem 'evenitron-ruby', :git => 'git@github.com:evenitron/evenitron-ruby.git'
14
+ gem 'evenitron'
9
15
  ```
10
16
 
11
- *We'll use the git references if possible until things are public.*
17
+ On start you need to configure the gem with your system key. In Rails this would go in `config\initializers\evenitron.rb`.
12
18
 
13
- Then you need to configure it within your `boot.rb` for Rails (I have no idea if this is the right file but it works). You can either do this explicitly:
19
+ ### delayed_job setup
14
20
 
15
21
  ```ruby
16
22
  require 'evenitron/delayed_job'
17
23
 
18
24
  Evenitron::DelayedJob.configure do
19
- system_key 'benchmarking'
20
- # collector_url 'http://localhost:5000/'
25
+ system_key 'your_key'
21
26
  end
22
27
  ```
23
28
 
@@ -29,28 +34,13 @@ require 'evenitron/delayed_job'
29
34
  Evenitron::DelayedJob.configure
30
35
  ```
31
36
 
32
- You must specify your system key either through configuration or the `EVENITRON_SYSTEM_KEY` environment variable but you will normally rely on the default collector URL of `https://collector.evenitron.com/`.
33
-
34
- # Setting up with resque
35
-
36
- There are two options for setting up Evenitron to monitor your delayed_job tasks.
37
-
38
- Firstly, add the gem to your Gemfile:
39
-
40
- ```ruby
41
- gem 'evenitron-ruby', :git => 'git@github.com:evenitron/evenitron-ruby.git'
42
- ```
43
-
44
- *We'll use the git references if possible until things are public.*
45
-
46
- Then you need to configure it within your `boot.rb` for Rails (I have no idea if this is the right file but it works). You can either do this explicitly:
37
+ ### resque setup
47
38
 
48
39
  ```ruby
49
40
  require 'evenitron/resque'
50
41
 
51
42
  Evenitron::Resque.configure do
52
- system_key 'benchmarking'
53
- # collector_url 'http://localhost:5000/'
43
+ system_key 'your_key'
54
44
  end
55
45
  ```
56
46
 
@@ -62,4 +52,6 @@ require 'evenitron/resque'
62
52
  Evenitron::Resque.configure
63
53
  ```
64
54
 
55
+ ## Notes
56
+
65
57
  You must specify your system key either through configuration or the `EVENITRON_SYSTEM_KEY` environment variable but you will normally rely on the default collector URL of `https://collector.evenitron.com/`.
@@ -5,8 +5,117 @@ require 'securerandom'
5
5
  require 'logger'
6
6
  require 'json'
7
7
 
8
- module Evenitron ; end
8
+ module Evenitron
9
+ require 'evenitron/logger'
10
+ require 'evenitron/collector_client'
11
+ require 'evenitron/collector'
12
+
13
+ # Public: convenience method for configuring the gem
14
+ #
15
+ # Examples
16
+ #
17
+ # Evenitron.configure do |config|
18
+ # config.system_key = 'your system key'
19
+ # end
20
+ #
21
+ # Raises StandardError if no system_key passed or found in env variable
22
+ def self.configure
23
+ yield self if block_given?
24
+
25
+ logger.error "Evenitron system_key required" && return unless self.system_key
26
+
27
+ initialize_delayed_job if Evenitron.delayed_job_present?
28
+ initialize_resque if Evenitron.resque_present?
29
+
30
+ logger.info "Evenitron configured with #{self.system_key} #{self.collector_url}"
31
+ end
32
+
33
+ # Public: configured logger
34
+ #
35
+ def self.logger
36
+ @logger ||= default_logger
37
+ end
38
+
39
+ # Public: configured connection url for the gem to use
40
+ #
41
+ def self.collector_url
42
+ @collector_url ||= (ENV['EVENITRON_COLLECTOR_URL'] || "http://collector.evenitron.com")
43
+ end
44
+
45
+ # Public: configured system_key for the gem to use
46
+ #
47
+ def self.system_key
48
+ @system_key ||= ENV['EVENITRON_SYSTEM_KEY']
49
+ end
50
+
51
+ class << self
52
+ # Public: Writers for the config values
53
+ #
54
+ attr_writer :logger, :collector_url, :system_key
55
+ end
56
+
57
+ # Public: util method for encoding values for network transport.
58
+ #
59
+ # values - Enumerable of Strings
60
+ #
61
+ # Returns clean, Base64 encoded String
62
+ def self.encode(*values)
63
+ value = values.join ':'
64
+ cleanse_base64 Base64.encode64(value)
65
+ end
66
+
67
+ # Public: generates a unique id fo a transaction
68
+ #
69
+ # length - length of the key (default: 64)
70
+ #
71
+ # Returns String id
72
+ def self.generate_id(length = 64)
73
+ cleanse_base64 SecureRandom.base64(length)
74
+ end
75
+
76
+ private
77
+
78
+ # Private: constructs a default STDOUT logger set to INFO
79
+ #
80
+ def self.default_logger
81
+ logger = ::Logger.new(STDOUT)
82
+ logger.level = ::Logger::INFO
83
+ logger
84
+ end
85
+
86
+ # Private: strips space and = from Base64 strings
87
+ #
88
+ # value - String to cleanse
89
+ #
90
+ # Returns cleansed String
91
+ def self.cleanse_base64(value)
92
+ value.gsub /[\s=]/, ''
93
+ end
94
+
95
+
96
+ # Public: delayed_job present?
97
+ #
98
+ def self.delayed_job_present?
99
+ Module.const_defined? 'Delayed'
100
+ end
101
+
102
+ # Public: resque present?
103
+ #
104
+ def self.resque_present?
105
+ Module.const_defined? 'Resque'
106
+ end
107
+
108
+ def self.initialize_delayed_job
109
+ require 'evenitron/delayed_job'
110
+ Evenitron::DelayedJob.register_lifecycle_hooks
111
+ logger.info "Configured Evenitron delayed_job hooks"
112
+ end
113
+
114
+ def self.initialize_resque
115
+ require 'evenitron/resque'
116
+ logger.info "Configured Evenitron resque hooks"
117
+ end
118
+
119
+ end
120
+
9
121
 
10
- require_relative 'evenitron/evenitron'
11
- require_relative 'evenitron/logger'
12
- require_relative 'evenitron/collector_client'
@@ -0,0 +1,14 @@
1
+ # Public: common methods for collectors
2
+ module Evenitron
3
+
4
+ module Collector
5
+
6
+ # Public: lazy load client with the configured values
7
+ # Expects the host method to expose a component method
8
+ def client
9
+ @_client ||= CollectorClient.new Evenitron.system_key, Evenitron.collector_url, component
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -1,14 +1,21 @@
1
+ # Public: Handles communication with the Evenitron collector
2
+ #
1
3
  module Evenitron
2
4
 
3
5
  class CollectorClient
4
6
 
5
7
  include Evenitron::Logger
6
8
 
7
- DEFAULT_URL = "https://collector.evenitron.com/"
8
-
9
- def initialize(system_key, url = DEFAULT_URL, component = nil)
10
- @system_key = system_key
11
- @url = url
9
+ # Public: constructor
10
+ #
11
+ # system_key - String key identifying the system
12
+ # url - String url of the collector to push messages to
13
+ # component - optional String identifying the component generating the message,
14
+ # Used to construct UserAgent for requests
15
+ #
16
+ # Returns a new instance of CollectorClient
17
+ def initialize(system_key, url, component = nil)
18
+ @system_key, @url = system_key, url
12
19
  @user_agent = 'evenitron-ruby 0.1'
13
20
  @user_agent = "#{@user_agent} (#{component})" if component
14
21
  @simulate_requests = ENV['EVENITRON_SIMULATE_REQUESTS'] == 'true'
@@ -37,6 +44,11 @@ module Evenitron
37
44
  send message(stage, args)
38
45
  end
39
46
 
47
+ # Private: sends the messages to the collector
48
+ #
49
+ # msg - Hash comprising all args to send
50
+ #
51
+ # Returns nothing
40
52
  def send(msg)
41
53
  json = msg.to_json
42
54
 
@@ -59,6 +71,13 @@ module Evenitron
59
71
  nil
60
72
  end
61
73
 
74
+ # Private: construct a hahs comprising the message elements by combining
75
+ # the optional args, stage, version and timestamp
76
+ #
77
+ # stage - Symbol or String indicating the stage of the transaction
78
+ # args - Hash of additional arguments to send
79
+ #
80
+ # Returns Hash
62
81
  def message(stage, args)
63
82
  msg = args.dup
64
83
  msg[:stage] = stage
@@ -1,112 +1,53 @@
1
- require_relative '../evenitron'
2
1
  require 'delayed_job'
2
+ require_relative 'delayed_job/collector'
3
3
 
4
4
  module Evenitron
5
5
 
6
6
  module DelayedJob
7
7
 
8
- def self.configure(&block)
9
- dj = Evenitron::DelayedJobCollector.new
10
- dj.configure &block unless block.nil?
11
- dj.start!
12
- nil
8
+ # Public: lazy load DelayedJob specific collector
9
+ #
10
+ def self.collector
11
+ @_collector ||= Evenitron::DelayedJob::Collector.new
13
12
  end
14
13
 
15
- end
16
-
17
- class DelayedJobCollector
18
-
19
- include Evenitron::Logger
20
-
21
- def initialize
22
- @collector_url = ENV['EVENITRON_COLLECTOR_URL']
23
- @system_key = ENV['EVENITRON_SYSTEM_KEY']
24
- end
25
-
26
- def configure(&block)
27
- instance_eval &block
28
- end
29
-
30
- def system_key(key)
31
- @system_key = key
32
- end
33
-
34
- def api_key(key)
35
- @api_key = key
36
- end
37
-
38
- def collector_url(url)
39
- @collector_url = url
40
- end
41
-
42
- def start!
43
- log.info "Registering delayed_job evenitron collector with system key #{@system_key} to send messages to #{@collector_url}"
44
- @client = Evenitron::CollectorClient.new @system_key, @collector_url, 'dj'
45
- register_lifecycle_hooks
46
- end
47
-
48
- private
49
-
50
- def lifecycle
14
+ def self.lifecycle
51
15
  Delayed::Worker.lifecycle
52
16
  end
53
17
 
54
- def register_lifecycle_hooks
55
- log.info "Registering delayed_job job queued callback"
18
+ # Public: wire up DelayedJob with hooks for generating collector messages
19
+ #
20
+ def self.register_lifecycle_hooks
21
+ Evenitron.logger.info "Registering delayed_job job queued callback"
56
22
  lifecycle.around :enqueue do |job, &block|
57
23
  result = block.call(job)
58
- job_queued job
24
+ Evenitron::DelayedJob.collector.job_queued job
59
25
  result
60
26
  end
61
27
 
62
- log.info "Registering delayed_job job failed callback"
28
+ Evenitron.logger.info "Registering delayed_job job failed callback"
63
29
  lifecycle.before :failure do |worker, job|
64
- job_failed job
30
+ Evenitron::DelayedJob.collector.job_failed job
65
31
  end
66
32
 
67
- log.info "Registering delayed_job job requeued callback"
33
+ Evenitron.logger.info "Registering delayed_job job requeued callback"
68
34
  lifecycle.after :error do |worker, job|
69
- job_queued job unless job.failed? or job.frozen?
35
+ Evenitron::DelayedJob.collector.job_queued job unless job.failed? or job.frozen?
70
36
  end
71
37
 
72
- log.info "Registering delayed_job job started callback"
73
- log.info "Registering delayed_job job complete callback"
38
+ Evenitron.logger.info "Registering delayed_job job started callback"
39
+ Evenitron.logger.info "Registering delayed_job job complete callback"
74
40
  lifecycle.around :perform do |worker, job, &block|
75
- job_started job, worker
41
+ Evenitron::DelayedJob.collector.job_started job, worker
76
42
  result = block.call(worker, job)
77
- job_complete job if result
43
+ Evenitron::DelayedJob.collector.job_complete job if result
78
44
  result
79
45
  end
80
- end
81
46
 
82
- def job_queued(job)
83
- id = get_id(job)
84
- log.info "Sending job queued message for job #{job.id} (evenitron id #{id})"
85
- @client.job_queued :id => id, :queue => job.class.table_name, :task => job.name
86
47
  end
87
-
88
- def job_started(job, worker)
89
- id = get_id(job)
90
- log.info "Sending job started message for job #{job.id} (evenitron id #{id})"
91
- @client.job_started :id => id, :worker => worker.name, :task => job.name
92
- end
93
-
94
- def job_failed(job)
95
- id = get_id(job)
96
- log.info "Sending job failed message for job #{job.id} (evenitron id #{id})"
97
- @client.job_failed :id => id, :task => job.name
98
- end
99
-
100
- def job_complete(job)
101
- id = get_id(job)
102
- log.info "Sending job complete message for job #{job.id} (evenitron id #{id})"
103
- @client.job_complete :id => id, :task => job.name
104
- end
105
-
106
- def get_id(job)
107
- Evenitron.encode job.class.table_name, job.id
108
- end
109
-
48
+
49
+ register_lifecycle_hooks if Evenitron.delayed_job_enabled?
50
+
110
51
  end
111
52
 
112
53
  end
@@ -0,0 +1,37 @@
1
+ module Evenitron
2
+
3
+ module DelayedJob
4
+
5
+ class Collector
6
+
7
+ include Evenitron::Collector
8
+
9
+ def component
10
+ "dj"
11
+ end
12
+
13
+ def job_queued(job)
14
+ client.job_queued :id => get_id(job), :queue => job.class.table_name, :task => job.name
15
+ end
16
+
17
+ def job_started(job, worker)
18
+ client.job_started :id => get_id(job), :worker => worker.name, :task => job.name
19
+ end
20
+
21
+ def job_failed(job)
22
+ client.job_failed :id => get_id(job), :task => job.name
23
+ end
24
+
25
+ def job_complete(job)
26
+ client.job_complete :id => get_id(job), :task => job.name
27
+ end
28
+
29
+ def get_id(job)
30
+ Evenitron.encode job.class.table_name, job.id
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -1,149 +1,19 @@
1
- require_relative '../evenitron'
1
+ # Public: coordinates loading of hooks for resque monitoring
2
+ #
2
3
  require 'resque'
4
+ require_relative 'resque/resque'
5
+ require_relative 'resque/collector'
3
6
 
4
7
  module Evenitron
5
8
 
6
- class NullResqueCollector
7
-
8
- include Evenitron::Logger
9
-
10
- def job_queued(queue, item)
11
- log.warn "EVENITRON NOT CONFIGURED : Not sending job queued message for job #{item['evenitron_id']}"
12
- end
13
-
14
- def job_started(job)
15
- log.info "EVENITRON NOT CONFIGURED : Not sending job started message for job #{job.evenitron_id}"
16
- end
17
-
18
- def job_failed(job)
19
- log.info "EVENITRON NOT CONFIGURED : Not sending job failed message for job #{job.evenitron_id}"
20
- end
21
-
22
- def job_complete(job)
23
- log.info "EVENITRON NOT CONFIGURED : Not sending job complete message for job #{job.evenitron_id}"
24
- end
25
-
26
- end
27
-
28
9
  module Resque
29
10
 
30
- @@collector = Evenitron::NullResqueCollector.new
31
-
32
- def self.configure(&block)
33
- rc = Evenitron::ResqueCollector.new
34
- rc.configure &block unless block.nil?
35
- rc.start!
36
- @@collector = rc
37
- nil
38
- end
39
-
11
+ # Public: lazy load resque specific collector
12
+ #
40
13
  def self.collector
41
- @@collector
42
- end
43
-
44
- end
45
-
46
- class ResqueCollector
47
-
48
- include Evenitron::Logger
49
-
50
- def initialize
51
- @collector_url = ENV['EVENITRON_COLLECTOR_URL']
52
- @system_key = ENV['EVENITRON_SYSTEM_KEY']
53
- end
54
-
55
- def configure(&block)
56
- instance_eval &block
57
- end
58
-
59
- def system_key(key)
60
- @system_key = key
61
- end
62
-
63
- def api_key(key)
64
- @api_key = key
65
- end
66
-
67
- def collector_url(url)
68
- @collector_url = url
69
- end
70
-
71
- def start!
72
- log.info "Registering resque evenitron collector with system key #{@system_key} to send messages to #{@collector_url}"
73
- @client = Evenitron::CollectorClient.new @system_key, @collector_url, 'resque'
74
- end
75
-
76
- def job_queued(queue, item)
77
- id = get_id(item)
78
- log.info "Sending job queued message for job #{id}"
79
- @client.job_queued :id => id, :queue => queue, :task => item[:class]
80
- end
81
-
82
- def job_started(job)
83
- log.info "Sending job started message for job #{job.evenitron_id}"
84
- @client.job_started :id => job.evenitron_id, :worker => job.worker.to_s, :task => job.payload['class']
85
- end
86
-
87
- def job_failed(job)
88
- log.info "Sending job failed message for job #{job.evenitron_id}"
89
- @client.job_failed :id => job.evenitron_id, :task => job.payload['class']
90
- end
91
-
92
- def job_complete(job)
93
- log.info "Sending job complete message for job #{job.evenitron_id}"
94
- @client.job_complete :id => job.evenitron_id, :task => job.payload['class']
95
- end
96
-
97
- private
98
-
99
- def get_id(item)
100
- item['evenitron_id']
14
+ @_collector ||= Evenitron::Resque::Collector.new
101
15
  end
102
16
 
103
17
  end
104
18
 
105
19
  end
106
-
107
- module Resque
108
-
109
- class << self
110
-
111
- alias :base_push :push
112
-
113
- def push(queue, item)
114
- item['evenitron_id'] ||= Evenitron.generate_id
115
- base_push(queue, item)
116
- Evenitron::Resque.collector.job_queued queue, item
117
- end
118
-
119
- end
120
-
121
- class Job
122
-
123
- alias :base_perform :perform
124
-
125
- def perform
126
- begin
127
- Evenitron.logger.debug "Processing job #{evenitron_id}"
128
- Evenitron::Resque.collector.job_started self
129
-
130
- result = base_perform
131
- Evenitron::Resque.collector.job_complete self
132
-
133
- Evenitron.logger.debug "Processed job #{evenitron_id}, result was #{result}"
134
- result
135
- rescue => e
136
- Evenitron.logger.fatal "Exception occurred whilst processing job #{evenitron_id}"
137
- Evenitron.logger.debug e.inspect
138
- Evenitron::Resque.collector.job_failed self
139
- raise e
140
- end
141
- end
142
-
143
- def evenitron_id
144
- @payload['evenitron_id']
145
- end
146
-
147
- end
148
-
149
- end
@@ -0,0 +1,39 @@
1
+ module Evenitron
2
+
3
+ module Resque
4
+
5
+ class Collector
6
+
7
+ include Evenitron::Collector
8
+
9
+ def component
10
+ "resque"
11
+ end
12
+
13
+ def job_queued(queue, item)
14
+ client.job_queued :id => get_id(item), :queue => queue, :task => item[:class]
15
+ end
16
+
17
+ def job_started(job)
18
+ client.job_started :id => job.evenitron_id, :worker => job.worker.to_s, :task => job.payload['class']
19
+ end
20
+
21
+ def job_failed(job)
22
+ client.job_failed :id => job.evenitron_id, :task => job.payload['class']
23
+ end
24
+
25
+ def job_complete(job)
26
+ client.job_complete :id => job.evenitron_id, :task => job.payload['class']
27
+ end
28
+
29
+ private
30
+
31
+ def get_id(item)
32
+ item['evenitron_id']
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,52 @@
1
+ # Public: Wraps resque methods and classes with hooks to generate messages
2
+ # to the collector
3
+ #
4
+ module Resque
5
+
6
+ class << self
7
+
8
+ alias :base_push :push
9
+
10
+ # Private: wraps the push method to decorate the job with an evenitron
11
+ # task id. Then generates a job queued message
12
+ def push(queue, item)
13
+ item['evenitron_id'] ||= Evenitron.generate_id
14
+ base_push(queue, item)
15
+ Evenitron::Resque.collector.job_queued queue, item
16
+ end
17
+ Evenitron.logger.info "Registered resque job queued callback"
18
+
19
+ end
20
+
21
+ class Job
22
+
23
+ alias :base_perform :perform
24
+
25
+ # Private: wraps the perform method to generate job processing messages
26
+ #
27
+ def perform
28
+ begin
29
+ Evenitron::Resque.collector.job_started self
30
+
31
+ result = base_perform
32
+ Evenitron::Resque.collector.job_complete self
33
+ result
34
+ rescue => e
35
+ Evenitron.logger.error "Exception occurred whilst processing job #{evenitron_id}"
36
+ Evenitron.logger.debug e.inspect
37
+ Evenitron::Resque.collector.job_failed self
38
+ raise e
39
+ end
40
+ end
41
+
42
+ Evenitron.logger.info "Registered resque job started callback"
43
+ Evenitron.logger.info "Registered resque job completed callback"
44
+ Evenitron.logger.info "Registered resque job failed callback"
45
+
46
+ def evenitron_id
47
+ @payload['evenitron_id']
48
+ end
49
+
50
+ end
51
+
52
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evenitron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-25 00:00:00.000000000Z
12
+ date: 2012-06-17 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
16
- requirement: &70228307216040 !ruby/object:Gem::Requirement
16
+ requirement: &70299044095340 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 1.6.7
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70228307216040
24
+ version_requirements: *70299044095340
25
25
  description: Ruby client for Evenitron with hooks for delayed_job and resque
26
26
  email:
27
27
  - support@evenitron.com
@@ -29,10 +29,13 @@ executables: []
29
29
  extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
+ - lib/evenitron/collector.rb
32
33
  - lib/evenitron/collector_client.rb
34
+ - lib/evenitron/delayed_job/collector.rb
33
35
  - lib/evenitron/delayed_job.rb
34
- - lib/evenitron/evenitron.rb
35
36
  - lib/evenitron/logger.rb
37
+ - lib/evenitron/resque/collector.rb
38
+ - lib/evenitron/resque/resque.rb
36
39
  - lib/evenitron/resque.rb
37
40
  - lib/evenitron.rb
38
41
  - README.markdown
@@ -1,32 +0,0 @@
1
- module Evenitron
2
-
3
- def self.logger
4
- @@logger ||= default_logger
5
- end
6
-
7
- def self.logger=(logger)
8
- @@logger = logger
9
- end
10
-
11
- def self.encode(*values)
12
- value = values.join ':'
13
- cleanse_base64 Base64.encode64(value)
14
- end
15
-
16
- def self.generate_id(length = 64)
17
- cleanse_base64 SecureRandom.base64(length)
18
- end
19
-
20
- private
21
-
22
- def self.default_logger
23
- logger = ::Logger.new(STDOUT)
24
- logger.level = ::Logger::INFO
25
- logger
26
- end
27
-
28
- def self.cleanse_base64(value)
29
- value.gsub /[\s=]/, ''
30
- end
31
-
32
- end