actv 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +30 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +64 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE +22 -0
- data/README.md +63 -0
- data/Rakefile +17 -0
- data/actv.gemspec +34 -0
- data/lib/actv.rb +48 -0
- data/lib/actv/address.rb +9 -0
- data/lib/actv/article.rb +89 -0
- data/lib/actv/article_search_results.rb +15 -0
- data/lib/actv/asset.rb +186 -0
- data/lib/actv/asset_channel.rb +13 -0
- data/lib/actv/asset_component.rb +8 -0
- data/lib/actv/asset_description.rb +15 -0
- data/lib/actv/asset_description_type.rb +12 -0
- data/lib/actv/asset_image.rb +13 -0
- data/lib/actv/asset_legacy_data.rb +20 -0
- data/lib/actv/asset_price.rb +11 -0
- data/lib/actv/asset_seo_url.rb +5 -0
- data/lib/actv/asset_status.rb +17 -0
- data/lib/actv/asset_tag.rb +9 -0
- data/lib/actv/asset_topic.rb +11 -0
- data/lib/actv/base.rb +148 -0
- data/lib/actv/channel.rb +13 -0
- data/lib/actv/client.rb +330 -0
- data/lib/actv/configurable.rb +39 -0
- data/lib/actv/default.rb +87 -0
- data/lib/actv/error.rb +32 -0
- data/lib/actv/error/bad_gateway.rb +11 -0
- data/lib/actv/error/bad_request.rb +10 -0
- data/lib/actv/error/client_error.rb +39 -0
- data/lib/actv/error/enhance_your_calm.rb +10 -0
- data/lib/actv/error/forbidden.rb +10 -0
- data/lib/actv/error/internal_server_error.rb +11 -0
- data/lib/actv/error/not_acceptable.rb +10 -0
- data/lib/actv/error/not_found.rb +10 -0
- data/lib/actv/error/server_error.rb +19 -0
- data/lib/actv/error/service_unavailable.rb +11 -0
- data/lib/actv/error/unauthorized.rb +10 -0
- data/lib/actv/event.rb +213 -0
- data/lib/actv/event_result.rb +8 -0
- data/lib/actv/event_search_results.rb +11 -0
- data/lib/actv/evergreen.rb +53 -0
- data/lib/actv/facet.rb +16 -0
- data/lib/actv/facet_term.rb +5 -0
- data/lib/actv/facet_value.rb +7 -0
- data/lib/actv/identity.rb +28 -0
- data/lib/actv/interest.rb +39 -0
- data/lib/actv/null_object.rb +19 -0
- data/lib/actv/phone_number.rb +9 -0
- data/lib/actv/place.rb +24 -0
- data/lib/actv/popular_interest.rb +18 -0
- data/lib/actv/popular_interest_search_results.rb +12 -0
- data/lib/actv/request/multipart_with_file.rb +37 -0
- data/lib/actv/response/parse_json.rb +29 -0
- data/lib/actv/response/raise_client_error.rb +21 -0
- data/lib/actv/response/raise_server_error.rb +18 -0
- data/lib/actv/search_results.rb +30 -0
- data/lib/actv/sub_event.rb +15 -0
- data/lib/actv/tag.rb +9 -0
- data/lib/actv/topic.rb +9 -0
- data/lib/actv/user.rb +38 -0
- data/lib/actv/version.rb +3 -0
- data/spec/actv/article_search_results_spec.rb +16 -0
- data/spec/actv/article_spec.rb +44 -0
- data/spec/actv/asset_channel_spec.rb +17 -0
- data/spec/actv/asset_description_spec.rb +17 -0
- data/spec/actv/asset_image_spec.rb +27 -0
- data/spec/actv/asset_price_spec.rb +23 -0
- data/spec/actv/asset_spec.rb +172 -0
- data/spec/actv/asset_status_spec.rb +24 -0
- data/spec/actv/base_spec.rb +51 -0
- data/spec/actv/client/articles_spec.rb +130 -0
- data/spec/actv/client/assets_spec.rb +87 -0
- data/spec/actv/client/event_results_spec.rb +35 -0
- data/spec/actv/client/events_spec.rb +99 -0
- data/spec/actv/client/search_spec.rb +58 -0
- data/spec/actv/client/system_health_spec.rb +16 -0
- data/spec/actv/client/users_spec.rb +31 -0
- data/spec/actv/client_spec.rb +140 -0
- data/spec/actv/event_spec.rb +331 -0
- data/spec/actv/evergreen_spec.rb +33 -0
- data/spec/actv/identifiable_spec.rb +31 -0
- data/spec/actv/null_object_spec.rb +24 -0
- data/spec/actv/place_spec.rb +25 -0
- data/spec/actv/search_results_spec.rb +44 -0
- data/spec/actv/user_spec.rb +25 -0
- data/spec/actv_spec.rb +60 -0
- data/spec/faraday/response_spec.rb +0 -0
- data/spec/fixtures/me.json +21 -0
- data/spec/fixtures/system_health.json +1 -0
- data/spec/fixtures/valid_article.json +187 -0
- data/spec/fixtures/valid_asset.json +185 -0
- data/spec/fixtures/valid_event.json +188 -0
- data/spec/fixtures/valid_event_results.json +23 -0
- data/spec/fixtures/valid_evergreen.json +1 -0
- data/spec/fixtures/valid_evergreen_child_1.json +1 -0
- data/spec/fixtures/valid_search.json +1282 -0
- data/spec/fixtures/valid_search_no_event_results.json +5 -0
- data/spec/fixtures/valid_search_no_results.json +9 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/helper.rb +43 -0
- metadata +432 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module ACTV
|
2
|
+
module Configurable
|
3
|
+
|
4
|
+
# Convenience method to allow configuration options to be set in a block
|
5
|
+
def configure
|
6
|
+
yield self
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
CONFIG_KEYS = [
|
11
|
+
:connection_options,
|
12
|
+
:endpoint,
|
13
|
+
:media_endpoint,
|
14
|
+
:middleware,
|
15
|
+
:search_endpoint,
|
16
|
+
:api_key
|
17
|
+
] unless defined? CONFIG_KEYS
|
18
|
+
|
19
|
+
attr_accessor *CONFIG_KEYS
|
20
|
+
|
21
|
+
AUTH_KEYS = [
|
22
|
+
:consumer_key,
|
23
|
+
:consumer_secret,
|
24
|
+
:oauth_token,
|
25
|
+
:oauth_token_secret,
|
26
|
+
] unless defined? AUTH_KEYS
|
27
|
+
|
28
|
+
attr_writer *AUTH_KEYS
|
29
|
+
|
30
|
+
class << self
|
31
|
+
|
32
|
+
def keys
|
33
|
+
@keys ||= CONFIG_KEYS + AUTH_KEYS
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/actv/default.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'actv/configurable'
|
3
|
+
require 'actv/request/multipart_with_file'
|
4
|
+
require 'actv/response/parse_json'
|
5
|
+
require 'actv/response/raise_client_error'
|
6
|
+
require 'actv/response/raise_server_error'
|
7
|
+
# require 'twitter/response/rate_limit'
|
8
|
+
require 'actv/version'
|
9
|
+
|
10
|
+
module ACTV
|
11
|
+
module Default
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def options
|
15
|
+
Hash[ACTV::Configurable.keys.map{|key| [key, send(key)]}]
|
16
|
+
end
|
17
|
+
|
18
|
+
# @note This is configurable in case you want to use HTTP instead of HTTPS or use a Active-compatible endpoint.
|
19
|
+
# @see http://en.blog.wordpress.com/2009/12/12/twitter-api/
|
20
|
+
# @see http://staff.tumblr.com/post/287703110/api
|
21
|
+
# @see http://developer.typepad.com/typepad-twitter-api/twitter-api.html
|
22
|
+
def endpoint
|
23
|
+
@endpoint ||= 'https://api.active.com'
|
24
|
+
end
|
25
|
+
|
26
|
+
def media_endpoint
|
27
|
+
@media_endpoint ||= 'https://upload.active.com'
|
28
|
+
end
|
29
|
+
|
30
|
+
def search_endpoint
|
31
|
+
@search_endpoint ||= 'https://search.active.com'
|
32
|
+
end
|
33
|
+
|
34
|
+
def connection_options
|
35
|
+
@connection_options ||= {
|
36
|
+
:headers => {
|
37
|
+
:accept => 'application/json',
|
38
|
+
:user_agent => "Active Ruby Gem #{ACTV::VERSION}"
|
39
|
+
},
|
40
|
+
:open_timeout => 5,
|
41
|
+
:raw => true,
|
42
|
+
:ssl => {:verify => false},
|
43
|
+
:timeout => 10,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
# @note Faraday's middleware stack implementation is comparable to that of Rack middleware. The order of middleware is important: the first middleware on the list wraps all others, while the last middleware is the innermost one.
|
48
|
+
# @see https://github.com/technoweenie/faraday#advanced-middleware-usage
|
49
|
+
# @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
|
50
|
+
def middleware
|
51
|
+
@middleware ||= Faraday::Builder.new(
|
52
|
+
&Proc.new do |builder|
|
53
|
+
builder.use ACTV::Request::MultipartWithFile # Convert file uploads to Faraday::UploadIO objects
|
54
|
+
builder.use Faraday::Request::Multipart # Checks for files in the payload
|
55
|
+
builder.use Faraday::Request::UrlEncoded # Convert request params as "www-form-urlencoded"
|
56
|
+
builder.use ACTV::Response::RaiseClientError # Handle 4xx server responses
|
57
|
+
builder.use ACTV::Response::ParseJson # Parse JSON response bodies using MultiJson
|
58
|
+
builder.use ACTV::Response::RaiseServerError # Handle 5xx server responses
|
59
|
+
# builder.use ACTV::Response::RateLimit # Update RateLimit object
|
60
|
+
builder.adapter Faraday.default_adapter # Set Faraday's HTTP adapter
|
61
|
+
end
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def consumer_key
|
66
|
+
ENV['ACTV_CONSUMER_KEY']
|
67
|
+
end
|
68
|
+
|
69
|
+
def consumer_secret
|
70
|
+
ENV['ACTV_CONSUMER_SECRET']
|
71
|
+
end
|
72
|
+
|
73
|
+
def oauth_token
|
74
|
+
ENV['ACTV_OAUTH_TOKEN']
|
75
|
+
end
|
76
|
+
|
77
|
+
def oauth_token_secret
|
78
|
+
ENV['ACTV_OAUTH_TOKEN_SECRET']
|
79
|
+
end
|
80
|
+
|
81
|
+
def api_key
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/actv/error.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module ACTV
|
2
|
+
# Custom error class for rescuing from all Twitter errors
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :wrapped_exception
|
5
|
+
|
6
|
+
def self.errors
|
7
|
+
@errors ||= Hash[descendants.map{|klass| [klass.const_get(:HTTP_STATUS_CODE), klass]}]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.descendants
|
11
|
+
ObjectSpace.each_object(::Class).select{|klass| klass < self}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Initializes a new Error object
|
15
|
+
#
|
16
|
+
# @param exception [Exception, String]
|
17
|
+
# @return [Twitter::Error]
|
18
|
+
def initialize(exception=$!)
|
19
|
+
if exception.respond_to?(:backtrace)
|
20
|
+
super(exception.message)
|
21
|
+
@wrapped_exception = exception
|
22
|
+
else
|
23
|
+
super(exception.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def backtrace
|
28
|
+
@wrapped_exception ? @wrapped_exception.backtrace : super
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'actv/error/server_error'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Error
|
5
|
+
# Raised when Active returns the HTTP status code 502
|
6
|
+
class BadGateway < ACTV::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 502
|
8
|
+
MESSAGE = "A3PI, or something it depends on (like Asset Service) is down."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'actv/error'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Error
|
5
|
+
# Raised when Active returns a 4xx HTTP status code or there's an error in Faraday
|
6
|
+
class ClientError < ACTV::Error
|
7
|
+
|
8
|
+
# Create a new error from an HTTP environment
|
9
|
+
#
|
10
|
+
# @param body [Hash]
|
11
|
+
# @return [ACTV::Error]
|
12
|
+
def self.from_response_body(body)
|
13
|
+
new(parse_error(body))
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def self.parse_error(body)
|
19
|
+
if body.nil?
|
20
|
+
''
|
21
|
+
elsif body[:error]
|
22
|
+
if body[:error].is_a? Hash
|
23
|
+
body[:error].fetch(:message, "")
|
24
|
+
else
|
25
|
+
body[:error]
|
26
|
+
end
|
27
|
+
elsif body[:errors]
|
28
|
+
first = Array(body[:errors]).first
|
29
|
+
if first.kind_of?(Hash)
|
30
|
+
first[:message].chomp
|
31
|
+
else
|
32
|
+
first.chomp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'actv/error/server_error'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Error
|
5
|
+
# Raised when Active returns the HTTP status code 500
|
6
|
+
class InternalServerError < ACTV::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 500
|
8
|
+
MESSAGE = "Something is technically wrong."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'actv/error'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Error
|
5
|
+
# Raised when Active returns a 5xx HTTP status code
|
6
|
+
class ServerError < ACTV::Error
|
7
|
+
MESSAGE = "Server Error"
|
8
|
+
|
9
|
+
# Initializes a new ServerError object
|
10
|
+
#
|
11
|
+
# @param message [String]
|
12
|
+
# @return [Twitter::Error::ServerError]
|
13
|
+
def initialize(message=nil)
|
14
|
+
super(message || self.class.const_get(:MESSAGE))
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'actv/error/server_error'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Error
|
5
|
+
# Raised when Active returns the HTTP status code 503
|
6
|
+
class ServiceUnavailable < ACTV::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 503
|
8
|
+
MESSAGE = "(__-){ Active is over capacity."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/actv/event.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'actv/asset'
|
2
|
+
|
3
|
+
module ACTV
|
4
|
+
class Event < ACTV::Asset
|
5
|
+
attr_reader :salesStartDate, :salesEndDate, :activityStartDate, :activityEndDate
|
6
|
+
alias sales_start_date salesStartDate
|
7
|
+
alias sales_end_date salesEndDate
|
8
|
+
alias activity_start_date activityStartDate
|
9
|
+
alias activity_end_date activityEndDate
|
10
|
+
|
11
|
+
def online_registration_available?
|
12
|
+
if is_present?(self.registrationUrlAdr)
|
13
|
+
if is_present?(self.legacy_data) && is_present?(self.legacy_data.onlineRegistration)
|
14
|
+
self.legacy_data.onlineRegistration.downcase == 'true'
|
15
|
+
else
|
16
|
+
true
|
17
|
+
end
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def registration_open?
|
24
|
+
if online_registration_available?
|
25
|
+
is_now_between? authoritative_reg_start_date, authoritative_reg_end_date
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def registration_closed?
|
32
|
+
if online_registration_available?
|
33
|
+
is_now_after? authoritative_reg_end_date
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def registration_not_yet_open?
|
40
|
+
if online_registration_available?
|
41
|
+
is_now_before? authoritative_reg_start_date
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def event_ended?
|
48
|
+
if is_present? activity_end_date
|
49
|
+
is_now_after? "#{activity_end_date.split('T').first}T23:59:59"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def registration_opening_soon?(time_in_days=3)
|
54
|
+
@reg_open_soon ||= begin
|
55
|
+
if online_registration_available?
|
56
|
+
if self.sales_start_date
|
57
|
+
if now_in_utc >= utc_time(self.sales_start_date) - time_in_days.days and
|
58
|
+
now_in_utc < utc_time(self.sales_start_date)
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def registration_closing_soon?(time_in_days=3)
|
69
|
+
@reg_closing_soon ||= begin
|
70
|
+
if online_registration_available?
|
71
|
+
if self.sales_end_date
|
72
|
+
if now_in_utc >= utc_time(self.sales_end_date) - time_in_days.days and
|
73
|
+
now_in_utc < utc_time(self.end_date)
|
74
|
+
return true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def display_close_date
|
84
|
+
@display_close_date ||= begin
|
85
|
+
val = tag_by_description 'displayclosedate'
|
86
|
+
if val
|
87
|
+
val.downcase == 'true'
|
88
|
+
else
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
alias display_close_date? display_close_date
|
94
|
+
|
95
|
+
############
|
96
|
+
|
97
|
+
# Returns the asset's registration open date
|
98
|
+
# in UTC. This is pulled from the salesStartDate
|
99
|
+
def registration_open_date
|
100
|
+
Time.parse "#{authoritative_reg_start_date} UTC"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns the asset's registration end date
|
104
|
+
# in UTC. This is pulled from the salesEndDate
|
105
|
+
def registration_close_date
|
106
|
+
Time.parse "#{authoritative_reg_end_date} UTC"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the asset's start date
|
110
|
+
# in UTC. This is pulled from the activityStartDate.
|
111
|
+
def event_start_date
|
112
|
+
Time.parse "#{activity_start_date} #{format_timezone_offset(timezone_offset)}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the asset's end date
|
116
|
+
# in UTC. This is pulled from the activityEndDate.
|
117
|
+
def event_end_date
|
118
|
+
Time.parse "#{activity_end_date} #{format_timezone_offset(timezone_offset)}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def timezone_offset
|
122
|
+
place.timezoneOffset + place.timezoneDST
|
123
|
+
end
|
124
|
+
|
125
|
+
############
|
126
|
+
|
127
|
+
def image_url
|
128
|
+
defaultImage = 'http://www.active.com/images/events/hotrace.gif'
|
129
|
+
image = ''
|
130
|
+
|
131
|
+
self.assetImages.each do |i|
|
132
|
+
if i.imageUrlAdr.downcase != defaultImage
|
133
|
+
image = i.imageUrlAdr
|
134
|
+
break
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
if image.blank?
|
139
|
+
if (self.logoUrlAdr && self.logoUrlAdr != defaultImage && self.logoUrlAdr =~ URI::regexp)
|
140
|
+
image = self.logoUrlAdr
|
141
|
+
end
|
142
|
+
end
|
143
|
+
image
|
144
|
+
end
|
145
|
+
|
146
|
+
alias online_registration? online_registration_available?
|
147
|
+
alias reg_open? registration_open?
|
148
|
+
alias reg_closed? registration_closed?
|
149
|
+
alias registration_not_open? registration_not_yet_open?
|
150
|
+
alias reg_not_open? registration_not_yet_open?
|
151
|
+
alias reg_not_yet_open? registration_not_yet_open?
|
152
|
+
alias ended? event_ended?
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# EG: -7 => "-0700"
|
157
|
+
def format_timezone_offset(offset)
|
158
|
+
(offset < 0 ? "-" : "") << offset.abs.to_s.rjust(2,'0') << '00'
|
159
|
+
end
|
160
|
+
|
161
|
+
def authoritative_reg_end_date
|
162
|
+
if is_present? sales_end_date
|
163
|
+
sales_end_date
|
164
|
+
elsif is_present? activity_end_date
|
165
|
+
"#{activity_end_date.split('T').first}T23:59:59"
|
166
|
+
elsif is_present? activity_start_date
|
167
|
+
activity_start_date
|
168
|
+
else
|
169
|
+
"2100-12-31T23:59:59"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def authoritative_reg_start_date
|
174
|
+
if is_present? sales_start_date
|
175
|
+
sales_start_date
|
176
|
+
else
|
177
|
+
"1970-01-01T00:00:00"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def is_now_before? date_string
|
182
|
+
now_in_utc < utc_time(date_string)
|
183
|
+
end
|
184
|
+
|
185
|
+
def is_now_after? date_string
|
186
|
+
!is_now_before? date_string
|
187
|
+
end
|
188
|
+
|
189
|
+
def is_now_between? start_date_string, end_date_string
|
190
|
+
utc_time(start_date_string) < now_in_utc && now_in_utc < utc_time(end_date_string)
|
191
|
+
end
|
192
|
+
|
193
|
+
def is_present? obj
|
194
|
+
!is_empty? obj
|
195
|
+
end
|
196
|
+
|
197
|
+
def is_empty? obj
|
198
|
+
obj.respond_to?(:empty?) ? obj.empty? : !obj
|
199
|
+
end
|
200
|
+
|
201
|
+
def now_in_utc
|
202
|
+
Time.now.utc
|
203
|
+
end
|
204
|
+
|
205
|
+
def utc_time(time_string)
|
206
|
+
return nil if time_string.nil? or time_string.empty?
|
207
|
+
return Time.parse(time_string).utc
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|