Class: Checkr::APIClass

Inherits:
Object
  • Object
show all
Defined in:
lib/checkr/api_class.rb

Direct Known Subclasses

APIList, APIResource, APISingleton

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id = nil) ⇒ APIClass

Returns a new instance of APIClass.



109
110
111
# File 'lib/checkr/api_class.rb', line 109

def initialize(id=nil)
  refresh_from(id)
end

Instance Attribute Details

#jsonObject

Returns the value of attribute json.



3
4
5
# File 'lib/checkr/api_class.rb', line 3

def json
  @json
end

Class Method Details

.api_class_method(name, method, path = nil, opts = {}) ⇒ Object



13
14
15
16
# File 'lib/checkr/api_class.rb', line 13

def self.api_class_method(name, method, path=nil, opts={})
  singleton = class << self; self end
  singleton.send(:define_method, name, api_lambda(name, method, path, opts))
end

.api_instance_method(name, method, path = nil, opts = {}) ⇒ Object



18
19
20
# File 'lib/checkr/api_class.rb', line 18

def self.api_instance_method(name, method, path=nil, opts={})
  self.send(:define_method, name, api_lambda(name, method, path, opts))
end

.api_lambda(out_name, out_method, out_path = nil, out_opts = {}) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/checkr/api_class.rb', line 224

def self.api_lambda(out_name, out_method, out_path=nil, out_opts={})
  # Path, Opts, and Klass are all optional, so we have to determine
  # which were provided using the criteria:
  temp = [out_path, out_opts]
  out_path = temp.select{ |t| t.is_a?(String) }.first || nil
  out_opts = temp.select{ |t| t.is_a?(Hash) }.first || {}

  out_arg_names = out_opts[:arguments] || []
  out_constructor = out_opts[:constructor] || :self
  out_default_params = out_opts[:default_params] || {}

  lambda do |*args|
    # Make sure we have clean data
    constructor = out_constructor
    method = out_method
    path = nil
    path = out_path.dup if out_path
    arg_names = nil
    arg_names = out_arg_names.dup if out_arg_names
    default_params = out_default_params # dont need to dup this since it isn't modified directly

    validate_args(arg_names, *args)
    arguments = compose_arguments(method, arg_names, *args)
    composed_path = compose_api_path(path, arguments, arguments[:params])
    unused_args = determine_unused_args(path, arg_names, arguments)
    arguments[:params] = compose_params(arguments[:params], unused_args, default_params)

    resp = Checkr.request(method, composed_path, arguments[:params], arguments[:opts])

    api_lambda_construct(resp, constructor, self)
  end
end

.api_lambda_construct(resp, constructor, this) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/checkr/api_class.rb', line 257

def self.api_lambda_construct(resp, constructor, this)
  case constructor
  when Class
    constructor.construct(resp)
  when Proc
    constructor.call(resp)
  when Symbol
    if constructor == :self
      this.construct(resp)
    else
      klass = Util.constantize(constructor)
      if klass
        klass.construct(resp)
      else
        raise ArgumentError.new("Invalid constructor. See method definition.")
      end
    end
  else
    this.construct(resp)
  end
end

.attribute(name, klass = nil, opts = {}) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/checkr/api_class.rb', line 22

def self.attribute(name, klass=nil, opts={})
  @attribute_names ||= Set.new
  @attribute_names << name.to_sym

  self.send(:define_method, "#{name}", attribute_get_lambda(name, opts))
  self.send(:define_method, "#{name}=", attribute_set_lambda(name, klass, opts))
end

.attribute_aliasesObject



59
60
61
62
63
64
65
66
# File 'lib/checkr/api_class.rb', line 59

def self.attribute_aliases
  @attribute_aliases ||= Set.new
  unless self == APIClass
    @attribute_aliases + self.superclass.attribute_aliases
  else
    @attribute_aliases
  end
end

.attribute_get_lambda(name, opts = {}) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/checkr/api_class.rb', line 182

def self.attribute_get_lambda(name, opts={})
  lambda do
    if !instance_variables_include?(name)
      if opts[:default]
        self.send("#{name}=", opts[:default])
        instance_variable_get("@#{name}")
      else
        nil
      end
    else
      instance_variable_get("@#{name}")
    end
  end
end

.attribute_namesObject



50
51
52
53
54
55
56
57
# File 'lib/checkr/api_class.rb', line 50

def self.attribute_names
  @attribute_names ||= Set.new
  unless self == APIClass
    @attribute_names + self.superclass.attribute_names
  else
    @attribute_names
  end
end

.attribute_set_lambda(name, klass = nil, opts = {}) ⇒ Object

TODO(joncalhoun): Add tests for this



198
199
200
201
202
203
204
205
206
# File 'lib/checkr/api_class.rb', line 198

def self.attribute_set_lambda(name, klass=nil, opts={})
  lambda do |val|
    if klass
      val = determine_attr_value(klass, val, opts)
    end
    instance_variable_set("@#{name}", val)
    mark_attribute_changed(name)
  end
end

.attribute_writer_alias(name, attr_name) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/checkr/api_class.rb', line 30

def self.attribute_writer_alias(name, attr_name)
  @attribute_aliases ||= Set.new
  @attribute_aliases << name.to_sym

  # self.send(:alias_method, name, attr_name)
  self.send(:alias_method, "#{name}=", "#{attr_name}=")
end

.attribute_writer_namesObject



68
69
70
# File 'lib/checkr/api_class.rb', line 68

def self.attribute_writer_names
  self.attribute_names + self.attribute_aliases
end

.changed_lambdaObject



101
102
103
104
105
106
107
# File 'lib/checkr/api_class.rb', line 101

def self.changed_lambda
  # This runs in the context of an instance since it is used in
  # an api_instance_method
  lambda do |instance|
    instance.changed_attributes
  end
end

.compose_api_path(path, arguments, params = {}, this = self) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/checkr/api_class.rb', line 342

def self.compose_api_path(path, arguments, params={}, this=self)
  # Setup the path using the following attribute order:
  #   1. Args passed in
  #   2. Args on this
  #   3. Args on this.class
  ret = (path || this.path || "").dup
  if ret.include?(":")
    missing = Set.new
    matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
    matches.each do |match|
      value = arguments[match]
      value ||= params[match] || params[match.to_s]
      begin
        value ||= this.send(match)
      rescue NoMethodError
      end
      begin
        value ||= this.class.send(match) unless this.class == Class
      rescue NoMethodError
      end

      if value.nil?
        missing << match
      end

      ret.sub!(match.inspect, "#{value}")
    end

    unless missing.empty?
      raise InvalidRequestError.new("Could not determine the full URL to request. Missing the following values: #{missing.to_a.join(', ')}.", "url")
    end
  end
  ret
end

.compose_arguments(method, arg_names, *args) ⇒ Object



326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/checkr/api_class.rb', line 326

def self.compose_arguments(method, arg_names, *args)
  arguments = {}
  names = arg_names.dup + [:params, :opts]

  names.each_with_index do |k, i|
    arguments[k] = args[i] if args.length > i
  end
  arguments[:params] ||= {}
  arguments[:opts] ||= {}

  arguments
end

.compose_params(params = {}, unused_args = {}, default_params = {}, this = self) ⇒ Object

Priority: params > unused_args > default_params



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/checkr/api_class.rb', line 307

def self.compose_params(params={}, unused_args={}, default_params={}, this=self)
  ret = {}

  # Handle the default params
  if default_params.is_a?(Proc)
    default_params = default_params.call(this)
  elsif default_params.is_a?(Symbol)
    default_params = this.send(default_params)
  end

  ret.update(default_params || {})
  ret.update(unused_args || {})
  ret.update(params || {})
  ret
end

.construct(json = {}) ⇒ Object



113
114
115
# File 'lib/checkr/api_class.rb', line 113

def self.construct(json={})
  self.new.refresh_from(json)
end

.determine_attr_value(klass, val, opts = {}, this = self) ⇒ Object

TODO(joncalhoun): Maybe make this delay calling nested constructors until the main obj is fully constructed otherwise.. for now code around it by references to parent in nested objects.



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/checkr/api_class.rb', line 209

def self.determine_attr_value(klass, val, opts={}, this=self)
  args = (opts && opts[:nested]) ? [val, this] : [val]
  if klass.is_a?(Proc)
    klass.call(*args)
  elsif klass.is_a?(Class)
    klass.construct(*args)
  else
    klass = Util.constantize(klass)
    klass.construct(*args)
  end
end

.determine_unused_args(path, arg_names, arguments, this = self) ⇒ Object

Raises:

  • (ArgumentError)


380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/checkr/api_class.rb', line 380

def self.determine_unused_args(path, arg_names, arguments, this=self)
  unused = Set.new(arg_names)
  path ||= this.path
  raise ArgumentError.new("Path has never been set") unless path
  if path.include?(":")
    matches = path.scan(/:([^\/]*)/).flatten.map(&:to_sym)
    matches.each{ |m| unused.delete(m) }
  end
  ret = {}
  unused.each do |arg_name|
    ret[arg_name] = arguments[arg_name]
  end
  ret
end

.pathObject

Raises:

  • (NotImplementedError)


5
6
7
# File 'lib/checkr/api_class.rb', line 5

def self.path
  raise NotImplementedError.new("APIClass is an abstract class. Please refer to its subclasses: #{subclasses}")
end

.register_subclass(subclass, name = nil) ⇒ Object



148
149
150
151
152
153
154
155
156
# File 'lib/checkr/api_class.rb', line 148

def self.register_subclass(subclass, name=nil)
  @subclasses ||= Set.new
  @subclasses << subclass

  unless name.nil?
    @subclasses_hash ||= {}
    @subclasses_hash[name] = subclass
  end
end

.subclass_fetch(name) ⇒ Object



141
142
143
144
145
146
# File 'lib/checkr/api_class.rb', line 141

def self.subclass_fetch(name)
  @subclasses_hash ||= {}
  if @subclasses_hash.has_key?(name)
    @subclasses_hash[name]
  end
end

.subclassesObject



137
138
139
# File 'lib/checkr/api_class.rb', line 137

def self.subclasses
  return @subclasses ||= Set.new
end

.validate_args(arg_names, *args) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/checkr/api_class.rb', line 282

def self.validate_args(arg_names, *args)
  # Make sure we have valid arguments
  if args.length > arg_names.length
    if args.length > arg_names.length + 2 # more than params and opts were included
      raise ArgumentError.new("Too many arguments")
    else
      # Params and opts are allowed, but they must be hashes
      args[arg_names.length..-1].each do |arg|
        unless arg.is_a?(Hash) || arg.nil?
          raise ArgumentError.new("Invalid Param or Opts argument")
        end
      end
    end
  end

  if args.length < arg_names.length
    missing = arg_names[args.length..-1]
    raise ArgumentError.new("Missing arguments #{missing}")
  end
end

Instance Method Details

#attributesObject



38
39
40
41
42
43
44
# File 'lib/checkr/api_class.rb', line 38

def attributes
  attributes = {}
  self.class.attribute_names.each do |attr|
    attributes[attr.to_sym] = self.send(attr)
  end
  attributes
end

#changed_attribute_namesObject



77
78
79
80
81
82
83
84
85
86
# File 'lib/checkr/api_class.rb', line 77

def changed_attribute_names
  @changed_attribute_names ||= Set.new
  attributes.each do |key, val|
    next if @changed_attribute_names.include?(key)
    if val.is_a?(Array) || val.is_a?(Hash)
      @changed_attribute_names << key if json[key] != val
    end
  end
  @changed_attribute_names
end

#changed_attributesObject



88
89
90
91
92
93
94
# File 'lib/checkr/api_class.rb', line 88

def changed_attributes
  ret = {}
  changed_attribute_names.each do |attr|
    ret[attr] = send(attr)
  end
  ret
end

#clear_changed_attributesObject



96
97
98
# File 'lib/checkr/api_class.rb', line 96

def clear_changed_attributes
  @changed_attribute_names = Set.new
end

#construct(json = {}) ⇒ Object

Alias, but dont declare it as one because we need to use overloaded methods.



133
134
135
# File 'lib/checkr/api_class.rb', line 133

def construct(json={})
  refresh_from(json)
end

#inspectObject



158
159
160
161
# File 'lib/checkr/api_class.rb', line 158

def inspect
  id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
  "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(attributes)
end

#mark_attribute_changed(attr_name) ⇒ Object



72
73
74
75
# File 'lib/checkr/api_class.rb', line 72

def mark_attribute_changed(attr_name)
  @changed_attribute_names ||= Set.new
  @changed_attribute_names << attr_name.to_sym
end

#non_nil_attributesObject



46
47
48
# File 'lib/checkr/api_class.rb', line 46

def non_nil_attributes
  attributes.select{|k, v| !v.nil? }
end

#pathObject

Raises:

  • (NotImplementedError)


9
10
11
# File 'lib/checkr/api_class.rb', line 9

def path
  raise NotImplementedError.new("APIClass is an abstract class. Please refer to its subclasses: #{APIClass.subclasses}")
end

#refresh_from(json = {}) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/checkr/api_class.rb', line 117

def refresh_from(json={})
  unless json.is_a?(Hash)
    json = { :id => json }
  end
  self.json = Util.sorta_deep_clone(json)

  json.each do |k, v|
    if self.class.attribute_writer_names.include?(k.to_sym)
      self.send("#{k}=", v)
    end
  end
  clear_changed_attributes
  self
end

#to_json(*a) ⇒ Object



167
168
169
# File 'lib/checkr/api_class.rb', line 167

def to_json(*a)
  JSON.generate(non_nil_attributes)
end

#to_s(*args) ⇒ Object



163
164
165
# File 'lib/checkr/api_class.rb', line 163

def to_s(*args)
  JSON.pretty_generate(non_nil_attributes)
end