mosaic-lyris 1.0.1

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.
Files changed (55) hide show
  1. data/Gemfile +3 -0
  2. data/README +4 -0
  3. data/Rakefile +31 -0
  4. data/VERSION +1 -0
  5. data/init.rb +4 -0
  6. data/lib/lyris.rb +2 -0
  7. data/lib/mosaic/lyris.rb +12 -0
  8. data/lib/mosaic/lyris/demographic.rb +65 -0
  9. data/lib/mosaic/lyris/filter.rb +6 -0
  10. data/lib/mosaic/lyris/list.rb +73 -0
  11. data/lib/mosaic/lyris/message.rb +95 -0
  12. data/lib/mosaic/lyris/object.rb +227 -0
  13. data/lib/mosaic/lyris/partner.rb +6 -0
  14. data/lib/mosaic/lyris/record.rb +101 -0
  15. data/lib/mosaic/lyris/trigger.rb +113 -0
  16. data/lib/mosaic/lyris/upload.rb +89 -0
  17. data/lib/mosaic/lyris_mailer.rb +102 -0
  18. data/mosaic-lyris.gemspec +143 -0
  19. data/test/demographic_test.rb +176 -0
  20. data/test/filter_test.rb +6 -0
  21. data/test/http_responder.rb +88 -0
  22. data/test/list_test.rb +49 -0
  23. data/test/message_test.rb +6 -0
  24. data/test/partner_test.rb +6 -0
  25. data/test/record_test.rb +177 -0
  26. data/test/responses/demographic/add_error_name_already_exists.xml +2 -0
  27. data/test/responses/demographic/add_success_12345.xml +2 -0
  28. data/test/responses/demographic/query_all_success.xml +19 -0
  29. data/test/responses/demographic/query_enabled_details_success.xml +12 -0
  30. data/test/responses/demographic/query_enabled_success.xml +13 -0
  31. data/test/responses/list/add_error_name_already_exists.xml +2 -0
  32. data/test/responses/list/add_success_12345.xml +2 -0
  33. data/test/responses/list/delete_error_not_found_99999.xml +2 -0
  34. data/test/responses/list/delete_success_12345.xml +2 -0
  35. data/test/responses/list/query_list_data_success.xml +20 -0
  36. data/test/responses/record/add_error_email_already_exists.xml +2 -0
  37. data/test/responses/record/add_success.xml +2 -0
  38. data/test/responses/record/query_all_success.xml +94 -0
  39. data/test/responses/record/query_all_success_empty.xml +2 -0
  40. data/test/responses/record/query_all_success_page_1.xml +54 -0
  41. data/test/responses/record/query_all_success_page_2.xml +34 -0
  42. data/test/responses/record/query_all_success_page_3.xml +14 -0
  43. data/test/responses/record/query_email_error_not_found.xml +2 -0
  44. data/test/responses/record/query_email_success_active.xml +14 -0
  45. data/test/responses/record/query_email_success_admin_trashed.xml +14 -0
  46. data/test/responses/record/query_email_success_bounced.xml +14 -0
  47. data/test/responses/record/query_email_success_unsubscribed.xml +14 -0
  48. data/test/responses/record/update_error_not_found.xml +2 -0
  49. data/test/responses/record/update_success.xml +2 -0
  50. data/test/responses/triggers/fire_error_invalid_recipients.xml +2 -0
  51. data/test/responses/triggers/fire_error_invalid_trigger_id.xml +2 -0
  52. data/test/responses/triggers/fire_success.xml +2 -0
  53. data/test/test_helper.rb +68 -0
  54. data/test/trigger_test.rb +45 -0
  55. metadata +342 -0
@@ -0,0 +1,6 @@
1
+ module Mosaic
2
+ module Lyris
3
+ class Partner < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,101 @@
1
+ module Mosaic
2
+ module Lyris
3
+ class Record < Object
4
+ attr_reader :demographics,
5
+ :doubleoptin,
6
+ :email,
7
+ :encoding,
8
+ :id,
9
+ :joindate,
10
+ :list_id,
11
+ :proof,
12
+ :state,
13
+ :statedate,
14
+ :trashed,
15
+ :trigger
16
+
17
+ class << self
18
+ def add(email, options = {})
19
+ validate_options!(options)
20
+ reply = post('record', 'add') do |request|
21
+ request.MLID options[:list_id] if options[:list_id]
22
+ put_data(request, 'email', email)
23
+ put_demographic_data(request, options[:demographics])
24
+ put_extra_data(request, 'trigger', 'yes') if options[:trigger]
25
+ put_extra_data(request, 'proof', 'yes') if options[:proof]
26
+ put_extra_data(request, 'state', options[:state])
27
+ put_extra_data(request, 'encoding', options[:encoding])
28
+ put_extra_data(request, 'doubleoptin', 'yes') if options[:doubleoptin]
29
+ end
30
+ new(options.merge(:id => reply.at('/DATASET/DATA').inner_html, :email => email, :state => options[:state] || 'active', :trashed => %w(bounced unsubscribed trashed).include?(options[:state].to_s)))
31
+ end
32
+
33
+ def query(what, options = {})
34
+ if /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i === what
35
+ query_one(what, options)
36
+ else
37
+ query_all(what, options)
38
+ end
39
+ end
40
+
41
+ def update(email, options = {})
42
+ validate_options!(options)
43
+ reply = post('record', 'update') do |request|
44
+ request.MLID options[:list_id] if options[:list_id]
45
+ put_data(request, 'email', email)
46
+ put_extra_data(request, 'new_email', options[:email])
47
+ put_demographic_data(request, options[:demographics])
48
+ put_extra_data(request, 'trigger', 'yes') if options[:trigger]
49
+ put_extra_data(request, 'proof', 'yes') if options[:proof]
50
+ put_extra_data(request, 'state', options[:state])
51
+ put_extra_data(request, 'encoding', options[:encoding])
52
+ end
53
+ # TODO: query full record? this is an incomplete snapshot of the updated record (ie. it only contains updated attributes/demographics)
54
+ new(options.merge(:id => reply.at('/DATASET/DATA').inner_html, :email => options[:email] || email, :state => options[:state], :trashed => options[:state] && %w(bounced unsubscribed trashed).include?(options[:state].to_s)))
55
+ end
56
+
57
+ protected
58
+ def query_all(what, options = {})
59
+ raise ArgumentError, "expected :all; got #{what.inspect}" unless %w(all).include?(what.to_s)
60
+ reply = post('record', 'query-listdata') do |request|
61
+ request.MLID options[:list_id] if options[:list_id]
62
+ put_extra_data(request, 'pagelimit', options[:per_page])
63
+ put_extra_data(request, 'page', options[:page] || 1) if options[:per_page]
64
+ put_extra_data(request, 'type', options[:state])
65
+ end
66
+ reply.search('/DATASET/RECORD').collect do |record|
67
+ new :demographics => get_demographic_data(record),
68
+ :email => get_data(record, 'email'),
69
+ :id => get_data(record, 'extra', nil, :id => 'uid'),
70
+ :list_id => options[:list_id],
71
+ :proof => get_boolean_data(record, 'extra', 'yes', nil, :id => 'proof'),
72
+ :state => get_data(record, 'extra', nil, :id => 'state') || 'active',
73
+ :statedate => get_date_data(record, 'extra', nil, :id => 'statedate'),
74
+ :trashed => get_boolean_data(record, 'extra', 'y', nil, :id => 'trashed')
75
+ end
76
+ end
77
+
78
+ def query_one(email, options = {})
79
+ reply = post('record', 'query-data') do |request|
80
+ request.MLID options[:list_id] if options[:list_id]
81
+ put_data(request, 'email', email)
82
+ end
83
+ record = reply.at('/DATASET/RECORD')
84
+ new :demographics => get_demographic_data(record),
85
+ :email => get_data(record, 'extra', nil, :id => 'email'),
86
+ :id => get_data(record, 'extra', nil, :id => 'uid'),
87
+ :joindate => get_time_data(record, 'extra', nil, :id => 'joindate'),
88
+ :list_id => options[:list_id],
89
+ :proof => get_boolean_data(record, 'extra', 'yes', nil, :id => 'proof'),
90
+ :state => get_data(record, 'extra', nil, :id => 'state') || 'active',
91
+ :statedate => get_date_data(record, 'extra', nil, :id => 'statedate'),
92
+ :trashed => get_boolean_data(record, 'extra', 'y', nil, :id => 'trashed')
93
+ end
94
+
95
+ def validate_options!(options)
96
+ raise ArgumentError, "expected state of :active, :bounced, :unsubscribed or :trashed; got #{options[:state]}" unless %w(active bounced unsubscribed trashed).include?(options[:state].to_s) if options[:state]
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,113 @@
1
+ require 'uri'
2
+
3
+ module Mosaic
4
+ module Lyris
5
+ class Trigger < Object
6
+ attr_reader :add,
7
+ :clickthru,
8
+ :enabled,
9
+ :id,
10
+ :from_email,
11
+ :from_name,
12
+ :message,
13
+ :message_id,
14
+ :message_text,
15
+ :name,
16
+ :not_sent,
17
+ :recipients_data,
18
+ :sent,
19
+ :subject,
20
+ :total_opened,
21
+ :unique_bounced,
22
+ :unique_click,
23
+ :unique_opened,
24
+ :unique_unsubscribed,
25
+ :total_sent
26
+
27
+ class << self
28
+ def fire(id, *recipients)
29
+ options = recipients.pop if recipients.last.is_a?(Hash)
30
+ list_id = options[:list_id] || default_list_id
31
+ id = lookup_trigger(id, :locale => options.delete(:locale)) if id.is_a?(Symbol)
32
+ reply = post('triggers', 'fire-trigger') do |request|
33
+ request.MLID list_id if list_id
34
+ put_extra_data(request, 'trigger_id', id)
35
+ put_extra_data(request, 'recipients', recipients.join(','))
36
+ put_extra_data(request, 'recipients_data', recipients_data_url(options[:recipients_data]))
37
+ put_extra_data(request, 'subject', options[:subject])
38
+ put_extra_data(request, 'clickthru', 'on') if options[:clickthru]
39
+ put_extra_data(request, 'add', 'yes') if options[:add]
40
+ put_extra_data(request, 'message', options[:message])
41
+ put_extra_data(request, 'message_text', options[:message_text])
42
+ put_extra_data(request, 'from_email', options[:from_email])
43
+ put_extra_data(request, 'from_name', options[:from_name])
44
+ end
45
+ sent = get_data(reply.at('/DATASET'), 'sent') || ''
46
+ not_sent = get_data(reply.at('/DATASET'), 'not sent') || ''
47
+ new options.merge(:id => id, :not_sent => not_sent.split(','), :sent => sent.split(','))
48
+ end
49
+
50
+ def lookup_trigger(key, options = {})
51
+ locale = options[:locale] || I18n.locale
52
+ locale = locale.to_s
53
+ key = key.to_s
54
+ if triggers[locale].is_a?(Hash) && triggers[locale][key]
55
+ triggers[locale][key]
56
+ else
57
+ triggers[key]
58
+ end
59
+ end
60
+
61
+ def query(what, options = {})
62
+ if what == :all
63
+ query_all(options)
64
+ else
65
+ query_one(what, options)
66
+ end
67
+ end
68
+
69
+ def recipients_data_url(recipients_data)
70
+ return if recipients_data.nil?
71
+ return recipients_data if URI.parse(recipients_data).scheme
72
+ url = URI.parse(callback_url)
73
+ url.path = recipients_data
74
+ url.to_s
75
+ end
76
+
77
+ protected
78
+ def query_all(options)
79
+ reply = post('triggers', 'query-listdata') do |request|
80
+ request.MLID options[:list_id] if options[:list_id]
81
+ end
82
+ reply.search('/DATASET/RECORD').collect do |record|
83
+ new :id => get_integer_data(record, 'trigger_id'),
84
+ :name => get_data(record, 'trigger_name'),
85
+ :enabled => get_boolean_data(record, 'trigger_enabled', 'on')
86
+ end
87
+ end
88
+
89
+ def query_one(id, options)
90
+ reply = post('triggers', 'query-data-summary') do |request|
91
+ request.MLID options[:list_id] if options[:list_id]
92
+ put_extra_data(request, 'trigger_id', id)
93
+ end
94
+ record = reply.at('/DATASET/RECORD')
95
+ new :id => get_integer_data(record, 'trigger_id'),
96
+ :message_id => get_integer_data(record, 'message_id'),
97
+ :spam_complaints => get_integer_data(record, 'spam_complaints'),
98
+ :subject => get_data(record, 'message_subject'),
99
+ :total_opened => get_integer_data(record, 'total_opened'),
100
+ :total_sent => get_integer_data(record, 'total_sent'),
101
+ :unique_opened => get_integer_data(record, 'unique_opened'),
102
+ :unique_click => get_integer_data(record, 'unique_click'),
103
+ :unique_unsubscribed => get_integer_data(record, 'unique_unsubscribed'),
104
+ :unique_bounced => get_integer_data(record, 'unique_bounced')
105
+ end
106
+ end
107
+
108
+ def enabled?
109
+ enabled
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,89 @@
1
+ module Mosaic
2
+ module Lyris
3
+ class Upload < Object
4
+ attr_reader :bytes_processed,
5
+ :completed_at,
6
+ :estimated_complete_at,
7
+ :list_id,
8
+ :size,
9
+ :status,
10
+ :time_elapsed,
11
+ :time_remaining
12
+
13
+ def done?
14
+ status == 'done'
15
+ end
16
+
17
+ def fatal?
18
+ status == 'fatal'
19
+ end
20
+
21
+ def active?
22
+ status =~ /^[0-9]+%$/
23
+ end
24
+
25
+ def incomplete?
26
+ pending? || active?
27
+ end
28
+
29
+ def pending?
30
+ status == 'pending'
31
+ end
32
+
33
+ def retry?
34
+ status == 'retry'
35
+ end
36
+
37
+ class << self
38
+ def add(email, file, options = {})
39
+ validate_options!(options)
40
+ reply = post('record', 'upload') do |request|
41
+ request.MLID options[:list_id] || ''
42
+ put_data(request, 'email', email)
43
+ put_extra_data(request, 'file', file)
44
+ put_extra_data(request, 'type', options[:type] || 'active')
45
+ put_extra_data(request, 'trigger', 'yes') if options[:trigger]
46
+ if options[:update]
47
+ put_extra_data(request, 'update', options[:update].to_s == 'only' ? 'only' : 'on')
48
+ put_extra_data(request, 'delete_blank', 'on') if options[:blank]
49
+ put_extra_data(request, 'untrash', 'on') if options[:untrash]
50
+ end
51
+ put_extra_data(request, 'validate', 'on') if options[:validate]
52
+ end
53
+ new(options.merge(:id => reply.at('/DATASET/DATA').inner_html, :email => options[:email], :file => file, :type => options[:type] || 'active'))
54
+ end
55
+
56
+ def build(record, options = {})
57
+ new :list_id => options[:list_id],
58
+ :file => get_element(record, 'FILE'),
59
+ :status => get_element(record, 'STATUS'),
60
+ :size => get_integer_element(record, 'SIZE'),
61
+ :bytes_processed => get_integer_element(record, 'PROCESSED'),
62
+ :time_elapsed => get_integer_element(record, 'ELAPSED'),
63
+ :time_remaining => get_integer_element(record, 'TIMELEFT'),
64
+ :estimated_complete_at => get_time_element(record, 'ETC'),
65
+ :completed_at => get_time_element(record, 'TIME')
66
+ end
67
+
68
+ def query(what, options = {})
69
+ reply = post('record', 'upload-status') do |request|
70
+ request.MLID options[:list_id] || ''
71
+ put_extra_data(request, 'file', what) unless what.to_s == 'all'
72
+ end
73
+ if what.to_s == 'all'
74
+ reply.search('/DATASET/*').collect do |record|
75
+ build record, options
76
+ end
77
+ else
78
+ build reply.at('/DATASET/DATASET_1'), options
79
+ end
80
+ end
81
+
82
+ def validate_options!(options)
83
+ raise ArgumentError, "expected type of :active, :proof, :unsubscribed, :bounced, :trashed or :globalunsubscribe; got #{options[:type]}" unless %w(active proof unsubscribed bounced trashed globalunsubscribe).include?(options[:type].to_s) if options[:type]
84
+ raise ArgumentError, "expected update value of true or :only; for #{options[:update]}" unless %w(true only).include?(options[:update].to_s) if options[:update]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,102 @@
1
+ module Mosaic
2
+ module LyrisMailer
3
+ include ActionView::Helpers::TagHelper
4
+ include ActionView::Helpers::TextHelper
5
+ include ActionView::Helpers::SanitizeHelper
6
+
7
+ private
8
+ if ActionMailer::Base.respond_to?(:add_delivery_method)
9
+ def friendly_from_for(mail)
10
+ mail[:from].display_names.first
11
+ end
12
+
13
+ def from_for(mail)
14
+ mail[:from].addresses.first
15
+ end
16
+ else
17
+ def friendly_from_for(mail)
18
+ mail.friendly_from
19
+ end
20
+
21
+ def from_for(mail)
22
+ mail.from
23
+ end
24
+ end
25
+
26
+ def get_lyris_html(mail)
27
+ if mail.multipart?
28
+ get_part_body(mail, 'text/html') || simple_format(get_part_body(mail, 'text/plain'))
29
+ elsif mail.content_type.start_with? 'text/html'
30
+ get_part_body(mail, 'text/html')
31
+ elsif mail.content_type.start_with? 'text/plain'
32
+ simple_format(mail.body.to_s)
33
+ else
34
+ raise TypeError, "unable to retrieve text/html for content type (#{mail.content_type})"
35
+ end
36
+ end
37
+
38
+ # TODO: handle encoding?
39
+ def get_lyris_options(mail)
40
+ lyris_options = {}
41
+ lyris_options[:subject] = mail.subject
42
+ lyris_options[:from_email] = from_for(mail)
43
+ lyris_options[:from_name] = friendly_from_for(mail)
44
+ lyris_options[:clickthru] = true
45
+ lyris_options[:message] = get_lyris_html(mail)
46
+ lyris_options[:message_text] = get_lyris_text(mail)
47
+ lyris_options
48
+ end
49
+
50
+ def get_lyris_text(mail)
51
+ if mail.multipart?
52
+ get_part_body(mail, 'text/plain') || ActionController::Base.helpers.strip_tags(get_part_body(mail, 'text/html'))
53
+ elsif mail.content_type.start_with? 'text/plain'
54
+ mail.body.to_s
55
+ elsif mail.content_type.start_with? 'text/html'
56
+ ActionController::Base.helpers.strip_tags(get_part_body(mail, 'text/html'))
57
+ else
58
+ raise TypeError, "unable to retrieve text/plain for content type (#{mail.content_type})"
59
+ end
60
+ end
61
+
62
+ def get_part(mail, content_type)
63
+ return mail if mail.parts.length == 0
64
+ mail.parts.find { |part| part.content_type.start_with? content_type }
65
+ end
66
+
67
+ def get_part_body(mail, content_type)
68
+ part = get_part(mail, content_type)
69
+ part.body.to_s if part
70
+ end
71
+
72
+ def perform_delivery_lyris(mail)
73
+ args = []
74
+ args << Mosaic::Lyris::Object.default_trigger_id
75
+ args += mail.destinations
76
+ args << get_lyris_options(mail)
77
+ trigger = Mosaic::Lyris::Trigger.fire(*args)
78
+ # TODO: deal with sent vs not sent
79
+ # raise "triggered email not sent" unless trigger.sent.include?(email)
80
+ end
81
+ end
82
+ end
83
+
84
+ if ActionMailer::Base.respond_to?(:add_delivery_method)
85
+ module Mosaic
86
+ class LyrisDeliveryMethod
87
+ include LyrisMailer
88
+
89
+ def initialize(values = {})
90
+ end
91
+
92
+ def deliver!(mail)
93
+ perform_delivery_lyris(mail)
94
+ end
95
+ end
96
+ end
97
+
98
+ ActionMailer::Base::add_delivery_method :lyris, Mosaic::LyrisDeliveryMethod
99
+ else
100
+ ActionMailer::Base.send :include, Mosaic::LyrisMailer
101
+ ActionMailer::Base.send :extend, ActionView::Helpers::SanitizeHelper::ClassMethods
102
+ end
@@ -0,0 +1,143 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mosaic-lyris}
8
+ s.version = "1.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["S. Brent Faulkner"]
12
+ s.date = %q{2013-03-07}
13
+ s.description = %q{A wrapper for the Lyris/EmailLabs API to simplify integration}
14
+ s.email = %q{brent.faulkner@mosaic.com}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "Gemfile",
20
+ "README",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "init.rb",
24
+ "lib/lyris.rb",
25
+ "lib/mosaic/lyris.rb",
26
+ "lib/mosaic/lyris/demographic.rb",
27
+ "lib/mosaic/lyris/filter.rb",
28
+ "lib/mosaic/lyris/list.rb",
29
+ "lib/mosaic/lyris/message.rb",
30
+ "lib/mosaic/lyris/object.rb",
31
+ "lib/mosaic/lyris/partner.rb",
32
+ "lib/mosaic/lyris/record.rb",
33
+ "lib/mosaic/lyris/trigger.rb",
34
+ "lib/mosaic/lyris/upload.rb",
35
+ "lib/mosaic/lyris_mailer.rb",
36
+ "mosaic-lyris.gemspec",
37
+ "test/demographic_test.rb",
38
+ "test/filter_test.rb",
39
+ "test/http_responder.rb",
40
+ "test/list_test.rb",
41
+ "test/message_test.rb",
42
+ "test/partner_test.rb",
43
+ "test/record_test.rb",
44
+ "test/responses/demographic/add_error_name_already_exists.xml",
45
+ "test/responses/demographic/add_success_12345.xml",
46
+ "test/responses/demographic/query_all_success.xml",
47
+ "test/responses/demographic/query_enabled_details_success.xml",
48
+ "test/responses/demographic/query_enabled_success.xml",
49
+ "test/responses/list/add_error_name_already_exists.xml",
50
+ "test/responses/list/add_success_12345.xml",
51
+ "test/responses/list/delete_error_not_found_99999.xml",
52
+ "test/responses/list/delete_success_12345.xml",
53
+ "test/responses/list/query_list_data_success.xml",
54
+ "test/responses/record/add_error_email_already_exists.xml",
55
+ "test/responses/record/add_success.xml",
56
+ "test/responses/record/query_all_success.xml",
57
+ "test/responses/record/query_all_success_empty.xml",
58
+ "test/responses/record/query_all_success_page_1.xml",
59
+ "test/responses/record/query_all_success_page_2.xml",
60
+ "test/responses/record/query_all_success_page_3.xml",
61
+ "test/responses/record/query_email_error_not_found.xml",
62
+ "test/responses/record/query_email_success_active.xml",
63
+ "test/responses/record/query_email_success_admin_trashed.xml",
64
+ "test/responses/record/query_email_success_bounced.xml",
65
+ "test/responses/record/query_email_success_unsubscribed.xml",
66
+ "test/responses/record/update_error_not_found.xml",
67
+ "test/responses/record/update_success.xml",
68
+ "test/responses/triggers/fire_error_invalid_recipients.xml",
69
+ "test/responses/triggers/fire_error_invalid_trigger_id.xml",
70
+ "test/responses/triggers/fire_success.xml",
71
+ "test/test_helper.rb",
72
+ "test/trigger_test.rb"
73
+ ]
74
+ s.homepage = %q{http://github.com/mosaicxm/mosaic-lyris}
75
+ s.require_paths = ["lib"]
76
+ s.rubygems_version = %q{1.3.6}
77
+ s.summary = %q{Lyris/EmailLabs API}
78
+
79
+ if s.respond_to? :specification_version then
80
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
81
+ s.specification_version = 3
82
+
83
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
84
+ s.add_runtime_dependency(%q<mosaic-lyris>, [">= 0"])
85
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
86
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
87
+ s.add_development_dependency(%q<mocha>, [">= 0"])
88
+ s.add_development_dependency(%q<mocha>, [">= 0"])
89
+ s.add_development_dependency(%q<mocha>, [">= 0"])
90
+ s.add_development_dependency(%q<mocha>, [">= 0"])
91
+ s.add_development_dependency(%q<mocha>, [">= 0"])
92
+ s.add_development_dependency(%q<mocha>, [">= 0"])
93
+ s.add_development_dependency(%q<mocha>, [">= 0"])
94
+ s.add_development_dependency(%q<mocha>, [">= 0"])
95
+ s.add_development_dependency(%q<mocha>, [">= 0"])
96
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
97
+ s.add_runtime_dependency(%q<active_support>, [">= 0"])
98
+ s.add_runtime_dependency(%q<htmlentities>, [">= 0"])
99
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
100
+ s.add_runtime_dependency(%q<tzinfo>, [">= 0"])
101
+ s.add_development_dependency(%q<mocha>, [">= 0"])
102
+ else
103
+ s.add_dependency(%q<mosaic-lyris>, [">= 0"])
104
+ s.add_dependency(%q<jeweler>, [">= 0"])
105
+ s.add_dependency(%q<jeweler>, [">= 0"])
106
+ s.add_dependency(%q<mocha>, [">= 0"])
107
+ s.add_dependency(%q<mocha>, [">= 0"])
108
+ s.add_dependency(%q<mocha>, [">= 0"])
109
+ s.add_dependency(%q<mocha>, [">= 0"])
110
+ s.add_dependency(%q<mocha>, [">= 0"])
111
+ s.add_dependency(%q<mocha>, [">= 0"])
112
+ s.add_dependency(%q<mocha>, [">= 0"])
113
+ s.add_dependency(%q<mocha>, [">= 0"])
114
+ s.add_dependency(%q<mocha>, [">= 0"])
115
+ s.add_dependency(%q<builder>, [">= 0"])
116
+ s.add_dependency(%q<active_support>, [">= 0"])
117
+ s.add_dependency(%q<htmlentities>, [">= 0"])
118
+ s.add_dependency(%q<nokogiri>, [">= 0"])
119
+ s.add_dependency(%q<tzinfo>, [">= 0"])
120
+ s.add_dependency(%q<mocha>, [">= 0"])
121
+ end
122
+ else
123
+ s.add_dependency(%q<mosaic-lyris>, [">= 0"])
124
+ s.add_dependency(%q<jeweler>, [">= 0"])
125
+ s.add_dependency(%q<jeweler>, [">= 0"])
126
+ s.add_dependency(%q<mocha>, [">= 0"])
127
+ s.add_dependency(%q<mocha>, [">= 0"])
128
+ s.add_dependency(%q<mocha>, [">= 0"])
129
+ s.add_dependency(%q<mocha>, [">= 0"])
130
+ s.add_dependency(%q<mocha>, [">= 0"])
131
+ s.add_dependency(%q<mocha>, [">= 0"])
132
+ s.add_dependency(%q<mocha>, [">= 0"])
133
+ s.add_dependency(%q<mocha>, [">= 0"])
134
+ s.add_dependency(%q<mocha>, [">= 0"])
135
+ s.add_dependency(%q<builder>, [">= 0"])
136
+ s.add_dependency(%q<active_support>, [">= 0"])
137
+ s.add_dependency(%q<htmlentities>, [">= 0"])
138
+ s.add_dependency(%q<nokogiri>, [">= 0"])
139
+ s.add_dependency(%q<tzinfo>, [">= 0"])
140
+ s.add_dependency(%q<mocha>, [">= 0"])
141
+ end
142
+ end
143
+