Module: SkullIsland::Helpers::Resource

Included in:
Resource
Defined in:
lib/skull_island/helpers/resource.rb

Overview

Simple helper methods for Resources

Instance Method Summary collapse

Instance Method Details

#<=>(other) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
# File 'lib/skull_island/helpers/resource.rb', line 253

def <=>(other)
  if id < other.id
    -1
  elsif id > other.id
    1
  elsif id == other.id
    0
  else
    raise Exceptions::InvalidArguments
  end
end

#datetime_from_params(params, actual_key) ⇒ Object



7
8
9
10
11
12
13
14
15
# File 'lib/skull_island/helpers/resource.rb', line 7

def datetime_from_params(params, actual_key)
  DateTime.new(
    params["#{actual_key}(1i)"].to_i,
    params["#{actual_key}(2i)"].to_i,
    params["#{actual_key}(3i)"].to_i,
    params["#{actual_key}(4i)"].to_i,
    params["#{actual_key}(5i)"].to_i
  )
end

#delayed_set(property, data, key = property.to_s) ⇒ Object

rubocop:disable Style/GuardClause rubocop:disable Security/Eval The delayed_set method allows a second phase of Erb templating immediately before sending data to the API. This allows the ‘lookup` function to work dynamically



21
22
23
24
25
26
27
28
29
# File 'lib/skull_island/helpers/resource.rb', line 21

def delayed_set(property, data, key = property.to_s)
  if data[key]
    value = recursive_erubi(data[key])
    send(
      "#{property}=".to_sym,
      value.is_a?(String) && value.start_with?('{"') ? eval(value) : value
    )
  end
end

#destroyObject



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/skull_island/helpers/resource.rb', line 175

def destroy
  raise Exceptions::ImmutableModification if immutable?

  unless new?
    @api_client.delete(relative_uri.to_s)
    @api_client.invalidate_cache_for(relative_uri.to_s)
    @lazy = false
    @tainted = true
    @entity.delete('id')
  end
  true
end

#digestObject

rubocop:enable Security/Eval rubocop:enable Style/GuardClause



46
47
48
49
50
# File 'lib/skull_island/helpers/resource.rb', line 46

def digest
  Digest::MD5.hexdigest(
    digest_properties.sort.map { |prp| "#{prp}=#{send(prp.to_sym) || ''}" }.compact.join(':')
  )
end

#digest_propertiesObject



52
53
54
55
# File 'lib/skull_island/helpers/resource.rb', line 52

def digest_properties
  props = properties.keys.reject { |k| i[created_at updated_at].include? k }
  supports_meta? ? props + [:project] : props
end

#find_by_digestObject

Tests for an existing version of this resource based on its properties rather than its ‘id`



58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/skull_island/helpers/resource.rb', line 58

def find_by_digest
  result = self.class.where(:digest, digest) # matching digest means the equivalent resource
  if result.size == 1
    entity_data = @api_client.cache(result.first.relative_uri.to_s) do |client|
      client.get(result.first.relative_uri.to_s)
    end
    @entity = entity_data
    @lazy = false
    @tainted = false
    true
  else
    false
  end
end

#fresh?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/skull_island/helpers/resource.rb', line 73

def fresh?
  !tainted?
end

#host_regexObject



77
78
79
# File 'lib/skull_island/helpers/resource.rb', line 77

def host_regex
  /^((\w|\w[\w\-]*\w)\.)*(\w|\w[\w\-]*\w)$/
end

#idObject



85
86
87
# File 'lib/skull_island/helpers/resource.rb', line 85

def id
  @entity[id_property.to_s]
end

#id_propertyObject



81
82
83
# File 'lib/skull_island/helpers/resource.rb', line 81

def id_property
  self.class.properties.select { |_, opts| opts[:id_property] }.keys.first || 'id'
end

#immutable?Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/skull_island/helpers/resource.rb', line 89

def immutable?
  self.class.immutable?
end

#import_update_or_skip(index:, verbose: false, test: false) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/skull_island/helpers/resource.rb', line 94

def import_update_or_skip(index:, verbose: false, test: false)
  if find_by_digest
    puts "[INFO] Skipping #{self.class} index #{index} (#{id})" if verbose
  elsif test
    puts "[INFO] Would have saved #{self.class} index #{index}"
  elsif modified_existing?
    puts "[INFO] Modified #{self.class} index #{index} (#{id})" if verbose
  elsif save
    puts "[INFO] Created #{self.class} index #{index} (#{id})" if verbose
  else
    puts "[ERR] Failed to save #{self.class} index #{index}"
  end
end

#lookup(type, value, raw = false) ⇒ Object

Looks up IDs (and usually wraps them in a Hash)



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/skull_island/helpers/resource.rb', line 111

def lookup(type, value, raw = false)
  id_value = case type
             when :ca_certificate
               Resources::CACertificate.find(:name, value).id
             when :certificate
               Resources::Certificate.find(:name, value).id
             when :consumer
               Resources::Consumer.find(:username, value).id
             when :route
               Resources::Route.find(:name, value).id
             when :service
               Resources::Service.find(:name, value).id
             when :upstream
               Resources::Upstream.find(:name, value).id
             else
               raise Exceptions::InvalidArguments, "#{type} is not a valid lookup type"
             end

  raw ? id_value : { 'id' => id_value }
end

#model_nameObject

ActiveRecord ActiveModel::Name compatibility method



133
134
135
# File 'lib/skull_island/helpers/resource.rb', line 133

def model_name
  self.class
end

#new?Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/skull_island/helpers/resource.rb', line 137

def new?
  !@entity.key?(id_property.to_s)
end

#persisted?Boolean

ActiveRecord ActiveModel::Model compatibility method

Returns:

  • (Boolean)


142
143
144
# File 'lib/skull_island/helpers/resource.rb', line 142

def persisted?
  !new?
end

#postprocess_created_at(value) ⇒ Object



146
147
148
# File 'lib/skull_island/helpers/resource.rb', line 146

def postprocess_created_at(value)
  Time.at(value).utc.to_datetime
end

#postprocess_updated_at(value) ⇒ Object



150
151
152
# File 'lib/skull_island/helpers/resource.rb', line 150

def postprocess_updated_at(value)
  Time.at(value).utc.to_datetime
end

#propertiesObject



154
155
156
# File 'lib/skull_island/helpers/resource.rb', line 154

def properties
  self.class.properties
end

#prune_for_save(data) ⇒ Object



265
266
267
268
269
270
271
272
# File 'lib/skull_island/helpers/resource.rb', line 265

def prune_for_save(data)
  data.reject do |k, v|
    k.to_sym == id_property ||
      !properties[k.to_sym] ||
      properties[k.to_sym][:read_only] ||
      v.nil?
  end
end

#recursive_erubi(data) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/skull_island/helpers/resource.rb', line 31

def recursive_erubi(data)
  case data
  when String
    eval(Erubi::Engine.new(data).src)
  when Array
    data.map { |item| recursive_erubi(item) }
  when Hash
    data.map { |k, v| [k, recursive_erubi(v)] }.to_h
  else
    data
  end
end

#reloadObject



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/skull_island/helpers/resource.rb', line 188

def reload
  if new?
    # Can't reload a new resource
    false
  else
    @api_client.invalidate_cache_for(relative_uri.to_s)
    entity_data = @api_client.cache(relative_uri.to_s) do |client|
      client.get(relative_uri.to_s)
    end
    @entity = entity_data
    @lazy = false
    @tainted = false
    true
  end
end

#required_propertiesObject



158
159
160
# File 'lib/skull_island/helpers/resource.rb', line 158

def required_properties
  properties.select { |_key, value| value[:required] }
end

#saveObject



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/skull_island/helpers/resource.rb', line 204

def save
  saveable_data = prune_for_save(@entity)
  validate_required_properties(saveable_data)

  if new?
    @entity  = @api_client.post(save_uri.to_s, saveable_data)
    @lazy    = true
  else
    @api_client.invalidate_cache_for(relative_uri.to_s)
    @entity = @api_client.patch(relative_uri, saveable_data)
  end
  @api_client.invalidate_cache_for(self.class.relative_uri.to_s) # clear any collection class
  @tainted = false
  true
rescue RestClient::BadRequest => e
  warn "[WARN] Failed to save #{self.class} via #{new? ? save_uri : relative_uri} with " \
       "'#{e.message}':\n#{saveable_data.to_yaml}\n\nReceived: #{e.inspect}"
end

#save_uriObject



223
224
225
# File 'lib/skull_island/helpers/resource.rb', line 223

def save_uri
  self.class.relative_uri
end

#supports_meta?Boolean

Returns:

  • (Boolean)


227
228
229
# File 'lib/skull_island/helpers/resource.rb', line 227

def supports_meta?
  false
end

#tainted?Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/skull_island/helpers/resource.rb', line 162

def tainted?
  @tainted ? true : false
end

#to_paramObject

ActiveRecord ActiveModel::Conversion compatibility method



167
168
169
# File 'lib/skull_island/helpers/resource.rb', line 167

def to_param
  new? ? nil : id.to_s
end

#to_sObject



171
172
173
# File 'lib/skull_island/helpers/resource.rb', line 171

def to_s
  to_param.to_s
end

#update(params) ⇒ Object

ActiveRecord ActiveModel compatibility method



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/skull_island/helpers/resource.rb', line 232

def update(params)
  new_params = {}
  # need to convert multi-part datetime params
  params.each do |key, value|
    if /([^(]+)\(1i/.match?(key)
      actual_key = key.match(/([^(]+)\(/)[1]
      new_params[actual_key] = datetime_from_params(params, actual_key)
    else
      new_params[key] = value
    end
  end

  new_params.each do |key, value|
    setter_key = "#{key}=".to_sym
    raise Exceptions::InvalidProperty unless respond_to?(setter_key)

    send(setter_key, value)
  end
  save
end