loading
Generated 2021-08-13T22:51:23+00:00

All Files ( 96.4% covered at 19.45 hits/line )

7 files in total.
139 relevant lines, 134 lines covered and 5 lines missed. ( 96.4% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/controllers/application_controller.rb 100.00 % 3 2 2 0 1.00
app/controllers/customers_controller.rb 93.18 % 89 44 41 3 1.68
app/controllers/vehicles_controller.rb 95.45 % 48 22 21 1 1.14
app/errors/customer_error.rb 100.00 % 1 1 1 0 1.00
app/models/application_record.rb 100.00 % 3 2 2 0 1.00
app/models/customer.rb 98.00 % 108 50 49 1 40.50
app/models/vehicle.rb 100.00 % 36 18 18 0 31.94

Controllers ( 94.12% covered at 1.49 hits/line )

3 files in total.
68 relevant lines, 64 lines covered and 4 lines missed. ( 94.12% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/controllers/application_controller.rb 100.00 % 3 2 2 0 1.00
app/controllers/customers_controller.rb 93.18 % 89 44 41 3 1.68
app/controllers/vehicles_controller.rb 95.45 % 48 22 21 1 1.14

Channels ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Models ( 98.57% covered at 37.17 hits/line )

3 files in total.
70 relevant lines, 69 lines covered and 1 lines missed. ( 98.57% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/models/application_record.rb 100.00 % 3 2 2 0 1.00
app/models/customer.rb 98.00 % 108 50 49 1 40.50
app/models/vehicle.rb 100.00 % 36 18 18 0 31.94

Mailers ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Helpers ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Jobs ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Libraries ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Ungrouped ( 100.0% covered at 1.0 hits/line )

1 files in total.
1 relevant lines, 1 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/errors/customer_error.rb 100.00 % 1 1 1 0 1.00

app/controllers/application_controller.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. 1 class ApplicationController < ActionController::API
  2. 1 ACTIVE_RECORD_EXS = [ActiveRecord::RecordInvalid, ActiveRecord::RecordNotFound]
  3. end

app/controllers/customers_controller.rb

93.18% lines covered

44 relevant lines. 41 lines covered and 3 lines missed.
    
  1. 1 class CustomersController < ApplicationController
  2. 1 SUPPORTED_SORTS = %w[full_name vehicle_type]
  3. 1 CUSTOMER_EXS = [CustomerError]
  4. 1 RETURNABLE_EXS = ACTIVE_RECORD_EXS + CUSTOMER_EXS
  5. 1 def index
  6. 5 sort_params = params.permit(:sort_by, :order)
  7. 5 sort_by = sort_params[:sort_by]
  8. 5 order = sort_params[:order]&.upcase || 'ASC'
  9. 5 if SUPPORTED_SORTS.include?(sort_by)
  10. 4 validated_sort(order, sort_by)
  11. else
  12. 1 render json: Customer.all
  13. end
  14. rescue *CUSTOMER_EXS => e
  15. render json: { "error" => e.message }
  16. end
  17. 1 def show
  18. 2 customer = Customer.find(params[:id])
  19. 1 render json: customer
  20. rescue ActiveRecord::RecordNotFound => e
  21. 1 render json: { "error" => e.message }
  22. end
  23. 1 def create
  24. 2 file = params.permit(:file)[:file]
  25. 2 if file && File.file?(file)
  26. 1 emails = Customer.file_create(file)
  27. 1 customers = Customer.where(email: emails)
  28. else
  29. 1 customers = Customer.create!(customer_params)
  30. end
  31. 2 render json: customers
  32. rescue *RETURNABLE_EXS => e
  33. render json: { "error" => e.message }
  34. end
  35. 1 def update
  36. 2 customer = Customer.find(params[:id])
  37. 1 customer.update!(customer_params)
  38. 1 render json: customer.reload
  39. rescue *RETURNABLE_EXS => e
  40. 1 render json: { "error" => e.message }
  41. end
  42. 1 def destroy
  43. 2 customer = Customer.find(params[:id])
  44. 1 customer.delete
  45. 1 render json: customer
  46. rescue ActiveRecord::RecordNotFound => e
  47. 1 render json: { "error" => e.message }
  48. end
  49. 1 private
  50. 1 def validated_sort(order, sort_by)
  51. 4 case sort_by
  52. when 'full_name'
  53. 2 sort_criteria = "first_name #{order}, last_name #{order}"
  54. 2 render json: Customer.order(sort_criteria)
  55. when 'vehicle_type'
  56. 2 sort_criteria = ['vehicles.vehicle_type', order].compact.join(' ')
  57. 2 render json: Customer
  58. .joins(:vehicles)
  59. .where('vehicles.primary = true')
  60. .order(sort_criteria)
  61. else
  62. raise CustomerError, 'Input invalid: invalid sort criteria'
  63. end
  64. end
  65. 1 def customer_params
  66. 2 params.require(:customer).permit(
  67. :first_name,
  68. :last_name,
  69. :full_name,
  70. :email
  71. )
  72. end
  73. end

app/controllers/vehicles_controller.rb

95.45% lines covered

22 relevant lines. 21 lines covered and 1 lines missed.
    
  1. 1 class VehiclesController < ApplicationController
  2. 1 def index
  3. 1 render json: Vehicle.all
  4. end
  5. 1 def show
  6. 2 render json: Vehicle.find(params[:id])
  7. rescue ActiveRecord::RecordNotFound => e
  8. 1 render json: { "error" => e.message }
  9. end
  10. 1 def create
  11. 1 render json: Vehicle.create!(vehicle_params)
  12. rescue ActiveRecord::RecordInvalid => e
  13. render json: { "error" => e.message }
  14. end
  15. 1 def update
  16. 2 vehicle = Vehicle.find(params[:id])
  17. 1 vehicle.update!(vehicle_params)
  18. 1 render json: vehicle.reload
  19. rescue *ACTIVE_RECORD_EXS => e
  20. 1 render json: { "error" => e.message }
  21. end
  22. 1 def destroy
  23. 2 vehicle = Vehicle.find(params[:id])
  24. 1 vehicle.delete
  25. 1 render json: vehicle
  26. rescue ActiveRecord::RecordNotFound => e
  27. 1 render json: { "error" => e.message }
  28. end
  29. 1 private
  30. 1 def vehicle_params
  31. 2 params.require(:vehicle).permit(
  32. :type,
  33. :name,
  34. :length,
  35. :customer_id
  36. )
  37. end
  38. end

app/errors/customer_error.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class CustomerError < StandardError; end

app/models/application_record.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. 1 class ApplicationRecord < ActiveRecord::Base
  2. 1 self.abstract_class = true
  3. end

app/models/customer.rb

98.0% lines covered

50 relevant lines. 49 lines covered and 1 lines missed.
    
  1. 1 class Customer < ApplicationRecord
  2. 1 attribute :id, :integer
  3. 1 attribute :first_name, :string
  4. 1 attribute :last_name, :string
  5. 1 attribute :full_name, :string
  6. 1 attribute :email, :string
  7. 1 validates :email, presence: true, uniqueness: true
  8. 1 validate :name_is_present
  9. 1 has_many :vehicles
  10. 52 has_one :vehicle, -> { where(primary: true) }
  11. 1 def attributes
  12. {
  13. 42 id: id,
  14. full_name: full_name,
  15. email: email,
  16. created_at: created_at,
  17. updated_at: updated_at,
  18. vehicle: vehicle
  19. }
  20. end
  21. 1 def full_name
  22. 94 [first_name, last_name].compact.join(' ')
  23. end
  24. 1 def full_name=(value)
  25. 9 params = value.split(' ')
  26. 9 assign_attributes first_name: params[0], last_name: params[1]
  27. end
  28. 1 def name_is_present
  29. 303 unless (first_name && last_name) || full_name
  30. errors.add(base: 'Validation failed: must have both first & last OR full_name present')
  31. end
  32. end
  33. 1 alias :name :full_name
  34. 1 class << self
  35. # file_create creates both customers
  36. # and a single associated vehicle from the given format
  37. 1 def file_create(file)
  38. 25 processed = []
  39. 25 lines = if file.respond_to? :tempfile
  40. 24 File.read(file.tempfile)
  41. 1 elsif file.is_a? String
  42. 1 File.read(file)
  43. end
  44. 25 lines.split("\n").each do |line|
  45. 98 processed << parse_customer(line)
  46. end
  47. 25 processed
  48. end
  49. 1 private
  50. 1 SPLIT_CRITERIA = /[|,]/
  51. 1 HEADER = {
  52. first_name: 0,
  53. last_name: 1,
  54. email: 2,
  55. type: 3,
  56. name: 4,
  57. length: 5
  58. }
  59. 1 def parse_customer(line)
  60. 98 params = line.split(SPLIT_CRITERIA)
  61. 98 customer = Customer.find_by(email: params[HEADER[:email]])
  62. 98 primary = false
  63. 98 unless customer
  64. 98 customer = Customer.create customer_file_params(params)
  65. 98 primary = true
  66. 98 customer.save
  67. end
  68. 98 if customer.valid?
  69. 97 vehicle = Vehicle.create vehicle_file_params(params, customer, primary)
  70. 97 vehicle.save
  71. 97 customer.email
  72. end
  73. end
  74. 1 def customer_file_params(params)
  75. {
  76. 98 first_name: params[HEADER[:first_name]],
  77. last_name: params[HEADER[:last_name]],
  78. email: params[HEADER[:email]],
  79. }
  80. end
  81. 1 def vehicle_file_params(params, customer, primary)
  82. {
  83. 97 type: params[HEADER[:type]],
  84. name: params[HEADER[:name]],
  85. length: params[HEADER[:length]],
  86. customer_id: customer.id,
  87. primary: primary
  88. }
  89. end
  90. end
  91. end

app/models/vehicle.rb

100.0% lines covered

18 relevant lines. 18 lines covered and 0 lines missed.
    
  1. 1 class Vehicle < ApplicationRecord
  2. 1 attribute :id, :integer
  3. 1 attribute :vehicle_type, :string
  4. 1 attribute :name, :string
  5. 1 attribute :length, :integer
  6. 1 attribute :primary, :boolean
  7. 1 attribute :customer_id, :integer
  8. 1 alias_attribute :type, :vehicle_type
  9. 1 validates :vehicle_type, :name, :length, :customer_id, presence: true
  10. 1 belongs_to :customer
  11. 1 def attributes
  12. {
  13. 55 id: id,
  14. type: type,
  15. name: name,
  16. length: length,
  17. created_at: created_at,
  18. updated_at: updated_at,
  19. customer_id: customer_id
  20. }
  21. end
  22. 1 def initialize(params)
  23. 102 params[:vehicle_type] = params[:type].downcase
  24. 102 params[:name] = params[:name].titleize
  25. 102 if Customer.find(params[:customer_id]).vehicles.size == 0
  26. 100 params[:primary] = true
  27. end
  28. 102 super(params.except(:type))
  29. end
  30. end