labclient 0.3.5 → 0.5.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/labclient/branches/branch.rb +1 -1
  3. data/lib/labclient/client/helpers.rb +116 -0
  4. data/lib/labclient/client/meta.rb +81 -0
  5. data/lib/labclient/client/setup.rb +53 -0
  6. data/lib/labclient/client.rb +41 -162
  7. data/lib/labclient/common.rb +9 -4
  8. data/lib/labclient/docs.rb +6 -1
  9. data/lib/labclient/epics/issues/remove.rb +1 -1
  10. data/lib/labclient/epics/issues/update.rb +1 -1
  11. data/lib/labclient/error.rb +9 -0
  12. data/lib/labclient/feature_flags/list.rb +1 -1
  13. data/lib/labclient/files/show.rb +5 -5
  14. data/lib/labclient/generator/template_helper.rb +4 -3
  15. data/lib/labclient/generator/wizard.rb +11 -12
  16. data/lib/labclient/groups/group.rb +0 -2
  17. data/lib/labclient/groups/search.rb +2 -6
  18. data/lib/labclient/groups/stub.rb +1 -0
  19. data/lib/labclient/http.rb +27 -9
  20. data/lib/labclient/issues/issue.rb +1 -1
  21. data/lib/labclient/klass.rb +10 -11
  22. data/lib/labclient/lab_struct.rb +39 -7
  23. data/lib/labclient/logger.rb +50 -0
  24. data/lib/labclient/merge_requests/delete.rb +10 -2
  25. data/lib/labclient/merge_requests/merge_request.rb +1 -3
  26. data/lib/labclient/notifications/update.rb +1 -1
  27. data/lib/labclient/overview.rb +40 -2
  28. data/lib/labclient/paginated_response.rb +0 -2
  29. data/lib/labclient/pipelines/pipeline.rb +1 -1
  30. data/lib/labclient/projects/methods.rb +6 -2
  31. data/lib/labclient/projects/reference.rb +2 -2
  32. data/lib/labclient/projects/search.rb +2 -6
  33. data/lib/labclient/repository/repository_tree.rb +7 -0
  34. data/lib/labclient/repository/tree.rb +1 -1
  35. data/lib/labclient/version.rb +7 -1
  36. data/lib/labclient.rb +8 -2
  37. metadata +66 -18
@@ -4,6 +4,7 @@ module LabClient
4
4
  # Helper to Generate Data / Populate GitLab
5
5
  class Wizard
6
6
  include Generator::Names # Name Generator
7
+ include LabClient::Logger
7
8
  attr_reader :client
8
9
  attr_accessor :count, :random, :password, :templates, :domain, :skip_confirmation
9
10
 
@@ -26,7 +27,7 @@ module LabClient
26
27
  self.random = true # Populate Random or use only Templates
27
28
  self.count = default_count
28
29
  self.password = SecureRandom.uuid
29
- puts "Default Password: #{password}" unless client.quiet?
30
+ logger.info('Wizard Default Password', password: password) unless client.quiet?
30
31
 
31
32
  self.skip_confirmation = true
32
33
  self.domain = URI.parse(client.settings[:url]).hostname
@@ -54,11 +55,11 @@ module LabClient
54
55
  end
55
56
 
56
57
  def generate_users
57
- puts 'Generating Users' unless client.quiet?
58
+ logger.info 'Generating Users' unless client.quiet?
58
59
  @users = @user_names.map do |name|
59
60
  username = name.downcase.gsub(/[^0-9A-Za-z]/, '')
60
61
  email = "#{username}@#{domain}"
61
- puts "User -- Name: #{name}, UserName: #{username}, Email: #{email}" unless client.quiet?
62
+ logger.info('User', name: name, username: username, email: email) unless client.quiet?
62
63
 
63
64
  client.users.create(
64
65
  name: name,
@@ -71,41 +72,39 @@ module LabClient
71
72
  end
72
73
 
73
74
  def generate_groups
74
- puts 'Generating Groups' unless client.quiet?
75
+ logger.info 'Generating Groups' unless client.quiet?
75
76
  @groups = @group_names.map do |name|
76
77
  path = name.downcase.gsub(/[^0-9A-Za-z]/, '')
77
- puts "Group -- #{name}/#{path}" unless client.quiet?
78
+ logger.info "Group -- #{name}/#{path}" unless client.quiet?
78
79
  client.groups.create(name: name, path: path)
79
80
  end
80
81
  end
81
82
 
82
- # rubocop:disable Metrics/AbcSize
83
83
  def generate_group_membership
84
- puts 'Adding Group members' unless client.quiet?
84
+ logger.info 'Adding Group members' unless client.quiet?
85
85
  ## Group Access Level
86
86
  @groups.each do |group|
87
87
  @users.sample(rand(1..@users.count)).each do |user|
88
88
  level = group.valid_group_project_levels.sample
89
- puts "Group Add: #{group.name}: #{user.name} - #{level}" unless client.quiet?
89
+ logger.info('Group Add', name: group.name, user: user.name, level: level) unless client.quiet?
90
90
  group.member_add(user, access_level: level)
91
91
  # :nocov:
92
92
  rescue StandardError => e
93
- puts e.message unless client.quiet?
93
+ logger.fatal e.message unless client.quiet?
94
94
  next
95
95
  # :nocov:
96
96
  end
97
97
  end
98
98
  end
99
- # rubocop:enable Metrics/AbcSize
100
99
 
101
100
  def generate_projects(group)
102
- puts 'Generating Projects' unless client.quiet?
101
+ logger.info 'Generating Projects' unless client.quiet?
103
102
  # Collect Group Members
104
103
  members = group.members
105
104
 
106
105
  # Loop through project names, create project add issues
107
106
  @project_names.uniq.map do |project_name|
108
- puts "Project: #{project_name}" unless client.quiet?
107
+ logger.info "Project: #{project_name}" unless client.quiet?
109
108
  project = group.project_create(name: project_name, description: gen_description)
110
109
 
111
110
  rand(count[:issues]).times do
@@ -1,4 +1,3 @@
1
- # rubocop:disable Metrics/ClassLength
2
1
  # Top namespace
3
2
  module LabClient
4
3
  # Inspect Helper
@@ -344,4 +343,3 @@ module LabClient
344
343
  # rubocop:enable Metrics/BlockLength
345
344
  end
346
345
  end
347
- # rubocop:enable Metrics/ClassLength
@@ -70,20 +70,16 @@ module LabClient
70
70
 
71
71
  private
72
72
 
73
- # rubocop:disable Metrics/CyclomaticComplexity
74
- # TODO - Finish Classes
73
+ # TODO: - Finish Classes
75
74
  def klass_type(scope)
76
75
  case scope
77
76
  when :projects then Project
78
77
  when :issues then Issue
79
78
  when :merge_requests then MergeRequest
80
- when :wiki_blobs then nil # wiki_blobs
81
- when :milestones then nil # ::Project::MileStone
82
- when :blobs then nil # blobs
79
+ when :milestones, :wiki_blobs, :blobs then nil
83
80
  when :commits then Commit
84
81
  when :users then User
85
82
  end
86
83
  end
87
- # rubocop:enable Metrics/CyclomaticComplexity
88
84
  end
89
85
  end
@@ -3,6 +3,7 @@ module LabClient
3
3
  # Ugly Hack, but Makes Docs Easy
4
4
  class Groups < Common
5
5
  doc '' do
6
+ # Do Nothing!
6
7
  end
7
8
  end
8
9
  end
@@ -72,9 +72,9 @@ module Typhoeus
72
72
  attr_reader :path, :client
73
73
 
74
74
  def data
75
- return @data if @data
75
+ @data ||= process_body
76
76
 
77
- @data = process_body
77
+ @data
78
78
  end
79
79
 
80
80
  # Shim for CurlHelper
@@ -88,19 +88,37 @@ module Typhoeus
88
88
  elsif headers['content-type']&.include? 'text/plain'
89
89
  body
90
90
  else
91
- result = Oj.load(body, mode: :compat, object_class: LabClient::LabStruct)
91
+ result = Oj.load(body, mode: :compat, symbol_keys: true, object_class: LabClient::LabStruct)
92
92
  result.instance_variable_set(:@response, self) if result.instance_of?(LabClient::LabStruct)
93
93
  result
94
94
  end
95
95
  end
96
96
 
97
+ # Retry Helper Accessor
98
+ def retry?
99
+ code == 429
100
+ end
101
+
102
+ # Print Error information
103
+ # 1. Use Typheous `return_message` if there isn't any return body
104
+ # For network/uri/dns related issues
105
+ # 2. Use body for parsed responses
106
+ # For Bad Request, invalid params
107
+ # 3. Return raw data
108
+ # For non body responses
109
+ def find_friendly_error
110
+ case data
111
+ when nil
112
+ return_message
113
+ when LabClient::LabStruct
114
+ data[:message] || data[:error]
115
+ else # Handle String as well
116
+ data
117
+ end
118
+ end
119
+
97
120
  def friendly_error
98
- message = if data
99
- data[:message] || data[:error] || data
100
- else
101
- return_message
102
- end
103
- "#{code} - #{message}"
121
+ "#{code} - #{find_friendly_error}"
104
122
  end
105
123
  end
106
124
  end
@@ -12,7 +12,7 @@ module LabClient
12
12
  date_time_attrs %i[closed_at created_at updated_at]
13
13
 
14
14
  # User Fields
15
- user_attrs %i[closed_by author assignee]
15
+ user_attrs %i[author assignee]
16
16
 
17
17
  # Via State Events
18
18
  def close
@@ -2,9 +2,10 @@
2
2
  module LabClient
3
3
  # Common Configuration for all Class Helpers
4
4
  class Klass < LabStruct
5
+ include LabClient::Logger
5
6
  include CurlHelper
6
7
 
7
- attr_reader :client, :response
8
+ attr_reader :client
8
9
 
9
10
  extend Docs
10
11
 
@@ -15,7 +16,6 @@ module LabClient
15
16
 
16
17
  # API Methods here have to be explicitly documented / custom helpers
17
18
  # Assume no methods by default
18
- # rubocop:disable Metrics/AbcSize
19
19
  def help(help_filter = nil)
20
20
  docs = LabClient::Docs.docs.dig(group_name, 'Reference')
21
21
  unless docs
@@ -38,7 +38,6 @@ module LabClient
38
38
  # Ignore Output
39
39
  nil
40
40
  end
41
- # rubocop:enable Metrics/AbcSize
42
41
 
43
42
  # Documented API Methods
44
43
  def api_methods
@@ -101,18 +100,18 @@ module LabClient
101
100
  self
102
101
  end
103
102
 
104
- # rubocop:disable Lint/MissingSuper
105
- def initialize(hash = nil, response = nil, client = nil)
103
+ # Quiet Reader Helper
104
+ def quiet?
105
+ client.quiet?
106
+ end
107
+
108
+ def initialize(table = nil, response = nil, client = nil)
109
+ # @table = table unless table.nil?
106
110
  @client = client
107
111
  @response = response
108
112
 
109
- @table = {}
110
- hash&.each_pair do |k, v|
111
- k = k.to_sym
112
- @table[k] = v
113
- end
113
+ super(table)
114
114
  end
115
- # rubocop:enable Lint/MissingSuper
116
115
 
117
116
  # Forward response success
118
117
  def success?
@@ -1,16 +1,28 @@
1
- # Extensions for OpenStruct specific to LabClient
1
+ # Extensions for LabStruct specific to LabClient
2
2
  module LabClient
3
- # Unique inherited class to not override top level openstruct
4
- class LabStruct < OpenStruct
3
+ # Unique inherited class to not override top level LabStruct
4
+ class LabStruct
5
5
  include CurlHelper
6
- attr_reader :response
6
+ attr_reader :response, :table
7
+
8
+ def initialize(hash = {})
9
+ @table = if hash.instance_of?(LabClient::LabStruct)
10
+ hash.to_h
11
+ else
12
+ hash
13
+ end
14
+ end
15
+
16
+ def to_h
17
+ @table
18
+ end
7
19
 
8
20
  def keys
9
- to_h.keys.sort
21
+ @table.keys.sort
10
22
  end
11
23
 
12
24
  def inspect
13
- to_h.inspect
25
+ @table.inspect
14
26
  end
15
27
 
16
28
  def as_json(*args)
@@ -18,7 +30,7 @@ module LabClient
18
30
  end
19
31
 
20
32
  def slice(*opts)
21
- to_h.slice(*opts)
33
+ @table.slice(*opts)
22
34
  end
23
35
 
24
36
  def client
@@ -29,5 +41,25 @@ module LabClient
29
41
  def success?
30
42
  @response.success?
31
43
  end
44
+
45
+ def method_missing(method, *_args)
46
+ @table[method] if @table.keys.include?(method)
47
+ end
48
+
49
+ def respond_to_missing?(method_name, include_private = false)
50
+ @table.keys.include?(method_name) || super
51
+ end
52
+
53
+ def key?(idx)
54
+ @table.key? idx
55
+ end
56
+
57
+ def []=(name, value)
58
+ @table[name] = value
59
+ end
60
+
61
+ def [](name)
62
+ @table[name.to_sym]
63
+ end
32
64
  end
33
65
  end
@@ -0,0 +1,50 @@
1
+ # Top Namespace
2
+ module LabClient
3
+ # Helper to Unify Log Output
4
+ module Logger
5
+ def logger
6
+ LabClient::Logger.logger
7
+ end
8
+
9
+ def self.logger_setup
10
+ logger = Ougai::Logger.new($stdout)
11
+ logger.formatter = Ougai::Formatters::LabClient.new
12
+
13
+ logger
14
+ end
15
+
16
+ def self.logger
17
+ @logger ||= logger_setup
18
+ end
19
+ end
20
+ end
21
+
22
+ # Log Formatter for Readable Inline (Timestamp)
23
+ # https://github.com/eropple/ougai-formatters-inline_readable/blob/master/lib/ougai/formatters/inline_readable.rb
24
+ module Ougai
25
+ module Formatters
26
+ # LabClient Specific
27
+ class LabClient < Ougai::Formatters::Readable
28
+ # For Amazing Print
29
+ def ai_settings
30
+ { ruby19_syntax: true, multiline: false }
31
+ end
32
+
33
+ def time_format
34
+ '%Y-%m-%e %k:%M:%S %z'
35
+ end
36
+
37
+ def call(severity, time, _progname, data)
38
+ msg = data.delete(:msg)
39
+ @excluded_fields.each { |f| data.delete(f) }
40
+
41
+ level = @plain ? severity : colored_level(severity)
42
+ output = "[#{time.strftime(time_format)}] #{level}: #{msg}"
43
+
44
+ output += " #{data.ai(ai_settings)}" unless data.empty?
45
+
46
+ "#{output}\n"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -13,13 +13,21 @@ module LabClient
13
13
  end
14
14
 
15
15
  doc 'Delete' do
16
- desc 'Delete through MergeRequest'
16
+ desc 'Delete via MergeRequest'
17
17
  example <<~DOC
18
- mr = client.merge_requests.show(343,2)
18
+ mr = client.merge_requests.delete(343,2)
19
19
  mr.delete
20
20
  DOC
21
21
  end
22
22
 
23
+ doc 'Delete' do
24
+ desc 'Via Project'
25
+ example <<~DOC
26
+ project = client.projects.show(1)
27
+ project.merge_requests_delete(12)
28
+ DOC
29
+ end
30
+
23
31
  # Delete
24
32
  def delete(project_id, merge_request_id)
25
33
  project_id = format_id(project_id)
@@ -1,7 +1,6 @@
1
1
  # Top namespace
2
2
  module LabClient
3
3
  # Inspect Helper
4
- # rubocop:disable Metrics/ClassLength
5
4
  class MergeRequest < Klass
6
5
  include ClassHelpers
7
6
  # extend Docs
@@ -193,7 +192,7 @@ module LabClient
193
192
  Timeout.timeout(total_time) do
194
193
  loop do
195
194
  reload
196
- puts "Waiting for Merge Status: #{merge_status}"
195
+ logger.info "Waiting for Merge Status: #{merge_status}" unless quiet?
197
196
  break if %w[can_be_merged unchecked].include? merge_status
198
197
  raise "Cannot be merged! #{import_error}" if %w[cannot_be_merged cannot_be_merged_recheck].include? merge_status
199
198
 
@@ -252,5 +251,4 @@ module LabClient
252
251
 
253
252
  # rubocop:enable Metrics/BlockLength
254
253
  end
255
- # rubocop:enable Metrics/ClassLength
256
254
  end
@@ -31,7 +31,7 @@ module LabClient
31
31
  example 'client.notifications.update_project(16, level: :custom)'
32
32
 
33
33
  result <<~DOC
34
- OpenStruct {
34
+ LabStruct {
35
35
  :level => "custom",
36
36
  :events => LabStruct {
37
37
  :new_release => nil,
@@ -1,5 +1,4 @@
1
1
  # The glorious gitlab labclient of labs
2
- # rubocop:disable Metrics/ClassLength
3
2
  module LabClient
4
3
  # Helper for Docs
5
4
  class Overview
@@ -362,6 +361,30 @@ module LabClient
362
361
  DOC
363
362
  end
364
363
 
364
+ doc 'Other' do
365
+ title 'Retry'
366
+ markdown <<~DOC
367
+ Gitlab.com and other instances may have protected paths or rate limiting enabled. By default the LabClient will:
368
+
369
+ 1. Check if a `Retry Later` request was received (Return Code 429)
370
+ 2. Wait for combined `retry-after` and `delay_factor` value
371
+ 3. Retry until retries `max` is reached
372
+ DOC
373
+
374
+ example <<~DOC
375
+ client = LabClient::Client.new(
376
+ url: 'https://gitlab.labclient',
377
+ token: 'gitlab api token',
378
+ retry: {
379
+ max: 3, delay_factor: 2
380
+ }
381
+ )
382
+
383
+ # Or after the init
384
+ @client.settings[:retry] = { max: 3, delay_factor: 2 }
385
+ DOC
386
+ end
387
+
365
388
  doc 'Other' do
366
389
  title 'Quiet'
367
390
  desc 'Error messages by default are printed to STDOUT. This can be supressed via the :quiet setting'
@@ -378,6 +401,22 @@ module LabClient
378
401
  DOC
379
402
  end
380
403
 
404
+ doc 'Other' do
405
+ title 'Debug'
406
+ desc 'Print Request Information'
407
+
408
+ example <<~DOC
409
+ client = LabClient::Client.new(
410
+ url: 'https://gitlab.labclient',
411
+ token: 'gitlab api token',
412
+ debug: true
413
+ )
414
+
415
+ # Or after the init
416
+ client.settings[:debug] = true
417
+ DOC
418
+ end
419
+
381
420
  doc 'Other' do
382
421
  title 'Curl'
383
422
  desc 'Sometimes you just wana have a curl example to test or send someone'
@@ -428,4 +467,3 @@ module LabClient
428
467
  end
429
468
  end
430
469
  end
431
- # rubocop:enable Metrics/ClassLength
@@ -40,9 +40,7 @@ module LabClient
40
40
  DOC
41
41
  end
42
42
 
43
- # rubocop:disable Lint/MissingSuper
44
43
  def respond_to_missing?(method_name, include_private = false); end
45
- # rubocop:enable Lint/MissingSuper
46
44
 
47
45
  def each_page(&block)
48
46
  yield @array # This will eventually be the whole list
@@ -63,7 +63,7 @@ module LabClient
63
63
  Timeout.timeout(total_time) do
64
64
  loop do
65
65
  reload
66
- puts "Waiting for Pipeline: #{status}"
66
+ logger.info('Waiting for Pipeline', status: status) unless quiet?
67
67
  break if %w[skipped manual canceled success].include? status
68
68
  raise 'Pipeline failed' if status == 'failed'
69
69
 
@@ -321,7 +321,7 @@ module LabClient
321
321
  end
322
322
 
323
323
  # Files
324
- def file(file_path, ref = :master, kind = nil)
324
+ def file(file_path, ref = :main, kind = nil)
325
325
  client.files.show(id, file_path, ref, kind)
326
326
  end
327
327
 
@@ -647,6 +647,10 @@ module LabClient
647
647
  client.merge_requests.create(id, query)
648
648
  end
649
649
 
650
+ def merge_request_delete(merge_request_iid)
651
+ client.merge_requests.delete(id, merge_request_iid)
652
+ end
653
+
650
654
  # Access Requests
651
655
  def request_access
652
656
  client.projects.access_requests.create(id)
@@ -748,7 +752,7 @@ module LabClient
748
752
  Timeout.timeout(total_time) do
749
753
  loop do
750
754
  reload
751
- puts "Waiting for Import Status: #{import_status}"
755
+ logger.info('Waiting for Import Status', status: import_status) unless quiet?
752
756
  break if %w[none finished].include? import_status
753
757
  raise "Import Failed: #{import_error}" if import_status == 'failed'
754
758
 
@@ -1,6 +1,6 @@
1
1
  # Top namespace
2
2
  module LabClient
3
- # rubocop:disable Metrics/BlockLength, Metrics/ClassLength
3
+ # rubocop:disable Metrics/BlockLength
4
4
  # Help Do Block
5
5
  class Project < Klass
6
6
  help do
@@ -268,5 +268,5 @@ module LabClient
268
268
  option 'parent', 'Show (API Call) Project Parent, returns either Group or User'
269
269
  end
270
270
  end
271
- # rubocop:enable Metrics/BlockLength, Metrics/ClassLength
271
+ # rubocop:enable Metrics/BlockLength
272
272
  end
@@ -72,20 +72,16 @@ module LabClient
72
72
 
73
73
  private
74
74
 
75
- # rubocop:disable Metrics/CyclomaticComplexity
76
- # TODO - Finish Classes
75
+ # TODO: - Finish Classes
77
76
  def klass_type(scope)
78
77
  case scope
79
78
  when :issues then Issue
80
79
  when :merge_requests then MergeRequest
81
80
  when :notes then Note
82
- when :wiki_blobs then nil # wiki_blobs
83
- when :milestones then nil # ::Project::MileStone
84
- when :blobs then nil # blobs
81
+ when :wiki_blobs, :milestones, :blobs then nil
85
82
  when :commits then Commit
86
83
  when :users then User
87
84
  end
88
85
  end
89
- # rubocop:enable Metrics/CyclomaticComplexity
90
86
  end
91
87
  end
@@ -0,0 +1,7 @@
1
+ # Top namespace
2
+ module LabClient
3
+ # Inspect Helper
4
+ class RepositoryTree < Klass
5
+ include ClassHelpers
6
+ end
7
+ end
@@ -29,7 +29,7 @@ module LabClient
29
29
  def tree(project_id, query = {})
30
30
  project_id = format_id(project_id)
31
31
 
32
- client.request(:get, "projects/#{project_id}/repository/tree", nil, query)
32
+ client.request(:get, "projects/#{project_id}/repository/tree", RepositoryTree, query)
33
33
  end
34
34
  end
35
35
  end
@@ -1,3 +1,9 @@
1
+ # Overall version
1
2
  module LabClient
2
- VERSION = '0.3.5'.freeze
3
+ VERSION = '0.5.1'.freeze
4
+
5
+ # Make it easy to print version
6
+ def self.version
7
+ VERSION
8
+ end
3
9
  end
data/lib/labclient.rb CHANGED
@@ -5,8 +5,10 @@ require 'active_support/all'
5
5
  require 'amazing_print'
6
6
  require 'gitlab_chronic_duration'
7
7
  require 'ostruct'
8
+ require 'ougai'
8
9
 
9
10
  require 'labclient/version'
11
+ require 'labclient/logger'
10
12
  require 'labclient/docs'
11
13
  require 'labclient/curl'
12
14
  require 'labclient/http'
@@ -769,6 +771,7 @@ require 'labclient/pipelines/pipeline'
769
771
  # Repository
770
772
  require 'labclient/repository/alias'
771
773
  require 'labclient/repository/tree'
774
+ require 'labclient/repository/repository_tree'
772
775
  require 'labclient/repository/blob'
773
776
  require 'labclient/repository/archive'
774
777
  require 'labclient/repository/compare'
@@ -874,7 +877,10 @@ require 'labclient/generator/generator'
874
877
  require 'labclient/generator/wizard'
875
878
 
876
879
  # Dynamically Require Templates (Simplify new template creation)
877
- Dir["#{File.dirname(__FILE__)}/labclient/generator/templates/*.rb"].sort.each { |file| require file }
880
+ Dir["#{File.dirname(__FILE__)}/labclient/generator/templates/*.rb"].each { |file| require file }
878
881
 
879
- # I am Very Last
882
+ # Load Client Files - I am Very Last!
883
+ require 'labclient/client/setup'
884
+ require 'labclient/client/helpers'
885
+ require 'labclient/client/meta'
880
886
  require 'labclient/client'