fixably 0.1.0

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +18 -0
  3. data/.gitignore +14 -0
  4. data/.mutant.yml +23 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +145 -0
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +119 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +227 -0
  12. data/Rakefile +12 -0
  13. data/bin/console +15 -0
  14. data/bin/mutant +3 -0
  15. data/bin/setup +8 -0
  16. data/docs/customer/child.md +23 -0
  17. data/docs/customer.md +78 -0
  18. data/docs/device.md +56 -0
  19. data/docs/order/line.md +27 -0
  20. data/docs/order/note.md +34 -0
  21. data/docs/order/task.md +27 -0
  22. data/docs/order.md +121 -0
  23. data/docs/queue.md +36 -0
  24. data/docs/status.md +42 -0
  25. data/docs/user.md +23 -0
  26. data/fixably.gemspec +38 -0
  27. data/lib/fixably/action_policy.rb +94 -0
  28. data/lib/fixably/actions.rb +148 -0
  29. data/lib/fixably/active_resource/base.rb +53 -0
  30. data/lib/fixably/active_resource/paginated_collection.rb +91 -0
  31. data/lib/fixably/application_resource.rb +49 -0
  32. data/lib/fixably/argument_parameterisation.rb +77 -0
  33. data/lib/fixably/authorization.rb +17 -0
  34. data/lib/fixably/config.rb +39 -0
  35. data/lib/fixably/create_has_many_record.rb +90 -0
  36. data/lib/fixably/encoding.rb +38 -0
  37. data/lib/fixably/load_from_response.rb +116 -0
  38. data/lib/fixably/logger.rb +33 -0
  39. data/lib/fixably/resource_lazy_loader.rb +76 -0
  40. data/lib/fixably/resources/customer.rb +64 -0
  41. data/lib/fixably/resources/device.rb +27 -0
  42. data/lib/fixably/resources/order.rb +87 -0
  43. data/lib/fixably/resources/queue.rb +9 -0
  44. data/lib/fixably/resources/status.rb +12 -0
  45. data/lib/fixably/resources/user.rb +15 -0
  46. data/lib/fixably/version.rb +5 -0
  47. data/lib/fixably.rb +37 -0
  48. metadata +217 -0
@@ -0,0 +1,34 @@
1
+ # Fixably::Order::Note
2
+
3
+ ## List order notes
4
+
5
+ ```ruby
6
+ Fixably::Order::Note.all(order_id: 1000)
7
+ ```
8
+
9
+ ## Get an order note
10
+
11
+ ```ruby
12
+ Fixably::Order::Note.find(1000, order_id: 1000)
13
+ Fixably::Order::Note.first(order_id: 1000)
14
+ Fixably::Order::Note.last(order_id: 1000)
15
+ ```
16
+
17
+ ## Create an order note
18
+
19
+ ```ruby
20
+ order = Fixably::Order.find(1_000)
21
+ note = Fixably::Order::Note.new(text: "New note", type: "INTERNAL")
22
+ order.notes << note
23
+
24
+ note.valid? # => true
25
+ note.persisted? # => true
26
+ ```
27
+
28
+ ## Update an order note
29
+
30
+ The Fixably API does not allow order notes to be updated
31
+
32
+ ## Destroy an order note
33
+
34
+ The Fixably API does not allow order notes to be destroyed
@@ -0,0 +1,27 @@
1
+ # Fixably::Order::Task
2
+
3
+ ## List order tasks
4
+
5
+ ```ruby
6
+ Fixably::Order::Task.all(order_id: 1000)
7
+ ```
8
+
9
+ ## Get an order task
10
+
11
+ ```ruby
12
+ Fixably::Order::Task.find(1000, order_id: 1000)
13
+ Fixably::Order::Task.first(order_id: 1000)
14
+ Fixably::Order::Task.last(order_id: 1000)
15
+ ```
16
+
17
+ ## Create an order task
18
+
19
+ The Fixably API does not allow order tasks to be created
20
+
21
+ ## Update an order task
22
+
23
+ While supported by Fixably, this feature has not yet been implemented
24
+
25
+ ## Destroy an order task
26
+
27
+ The Fixably API does not allow notes to be destroyed
data/docs/order.md ADDED
@@ -0,0 +1,121 @@
1
+ # Fixably::Order
2
+
3
+ ## List orders
4
+
5
+ ```ruby
6
+ Fixably::Order.all
7
+ Fixably::Order.where(is_closed: false)
8
+ Fixably::Order.where(updated_at: "2000-01-01")
9
+ Fixably::Order.where(updated_at: ["2000-01-01",]) # >= 2000-01-01
10
+ Fixably::Order.where(updated_at: [,"2000-01-01"]) # <= 2000-01-01
11
+ Fixably::Order.where(updated_at: ["2000-01-01","2000-02-01"]) # 2000-01-01 <> 2000-02-01
12
+ ```
13
+
14
+ ### Include associations in a list
15
+
16
+ **Contact**
17
+ ```ruby
18
+ Fixably::Order.includes(:contact).all
19
+ Fixably::Order.includes(:contact).where(internal_location: "SERVICE")
20
+ ```
21
+
22
+ **Customer**
23
+ ```ruby
24
+ Fixably::Order.includes(:customer).all
25
+ Fixably::Order.includes(:customer).where(internal_location: "SERVICE")
26
+ ```
27
+
28
+ **Device**
29
+ ```ruby
30
+ Fixably::Order.includes(:device).all
31
+ Fixably::Order.includes(:device).where(internal_location: "SERVICE")
32
+ ```
33
+
34
+ **Handled by**
35
+ ```ruby
36
+ Fixably::Order.includes(:handled_by).all
37
+ Fixably::Order.includes(:handled_by).where(internal_location: "SERVICE")
38
+ ```
39
+
40
+ **Ordered by**
41
+ ```ruby
42
+ Fixably::Order.includes(:ordered_by).all
43
+ Fixably::Order.includes(:ordered_by).where(internal_location: "SERVICE")
44
+ ```
45
+
46
+ **Queue**
47
+ ```ruby
48
+ Fixably::Order.includes(:queue).all
49
+ Fixably::Order.includes(:queue).where(internal_location: "SERVICE")
50
+ ```
51
+
52
+ **Status**
53
+ ```ruby
54
+ Fixably::Order.includes(:status).all
55
+ Fixably::Order.includes(:status).where(internal_location: "SERVICE")
56
+ ```
57
+
58
+ **Lines**
59
+ ```ruby
60
+ Fixably::Order.includes(:lines).all
61
+ Fixably::Order.includes(:lines).where(internal_location: "SERVICE")
62
+ ```
63
+
64
+ **Notes**
65
+ ```ruby
66
+ Fixably::Order.includes(:notes).all
67
+ Fixably::Order.includes(:notes).where(internal_location: "SERVICE")
68
+ ```
69
+
70
+ **Tasks**
71
+ ```ruby
72
+ Fixably::Order.includes(:tasks).all
73
+ Fixably::Order.includes(:tasks).where(internal_location: "SERVICE")
74
+ ```
75
+
76
+ ## Get a customer
77
+
78
+ ```ruby
79
+ Fixably::Order.find(1_000)
80
+ Fixably::Order.first
81
+ Fixably::Order.last
82
+ ```
83
+
84
+ ## Create an order
85
+
86
+ ```ruby
87
+ order = Fixably::Order.create(internal_location: "SERVICE")
88
+ customer.valid? # => true
89
+ customer.persisted? # => true
90
+
91
+ # Raises an error on failure
92
+ Fixably::Order.create!(internal_location: "SERVICE")
93
+
94
+ order = Fixably::Order.new(internal_location: "SERVICE")
95
+ order.save
96
+ order.valid? # => true
97
+ order.persisted? # => true
98
+
99
+ order = Fixably::Order.new(internal_location: "SERVICE")
100
+ # Raises an error on failure
101
+ order.save!
102
+ ```
103
+
104
+ ## Update an order
105
+
106
+ ```ruby
107
+ customer = Fixably::Customer.first
108
+ order = Fixably::Order.find(1_000)
109
+ order.internal_location = "SERVICE"
110
+
111
+ order.save
112
+ order.valid? # => true
113
+ order.persisted? # => true
114
+
115
+ # Raises an error on failure
116
+ order.save!
117
+ ```
118
+
119
+ ## Destroy an order
120
+
121
+ The Fixably API does not allow orders to be destroyed
data/docs/queue.md ADDED
@@ -0,0 +1,36 @@
1
+ # Fixably::Queue
2
+
3
+ ## List queue
4
+
5
+ ```ruby
6
+ Fixably::Queue.all
7
+ Fixably::Queue.where(name: "Mac")
8
+ ```
9
+
10
+ ### Include associations in a list
11
+
12
+ **Statuses**
13
+ ```ruby
14
+ Fixably::Queue.includes(:statuses).all
15
+ Fixably::Queue.includes(:statuses).where(name: "Mac")
16
+ ```
17
+
18
+ ## Get a queue
19
+
20
+ ```ruby
21
+ Fixably::Queue.find(1_000)
22
+ Fixably::Queue.first
23
+ Fixably::Queue.last
24
+ ```
25
+
26
+ ## Create a queue
27
+
28
+ The Fixably API does not allow queues to be created
29
+
30
+ ## Update a queue
31
+
32
+ The Fixably API does not allow queues to be updated
33
+
34
+ ## Destroy a queue
35
+
36
+ The Fixably API does not allow queues to be destroyed
data/docs/status.md ADDED
@@ -0,0 +1,42 @@
1
+ # Fixably::Status
2
+
3
+ ## List status
4
+
5
+ ```ruby
6
+ Fixably::Status.all
7
+ Fixably::Status.where(serial_number: "ABCDE123FGHI")
8
+ ```
9
+
10
+ ### Include associations in a list
11
+
12
+ **Custom**
13
+ ```ruby
14
+ Fixably::Status.includes(:custom).all
15
+ Fixably::Status.includes(:custom).where(serial_number: "ABCDE123FGHI")
16
+ ```
17
+
18
+ **Queue**
19
+ ```ruby
20
+ Fixably::Status.includes(:queue).all
21
+ Fixably::Status.includes(:queue).where(serial_number: "ABCDE123FGHI")
22
+ ```
23
+
24
+ ## Get a status
25
+
26
+ ```ruby
27
+ Fixably::Status.find(1_000)
28
+ Fixably::Status.first
29
+ Fixably::Status.last
30
+ ```
31
+
32
+ ## Create a status
33
+
34
+ The Fixably API does not allow statuses to be created
35
+
36
+ ## Update a status
37
+
38
+ The Fixably API does not allow statuses to be updated
39
+
40
+ ## Destroy a status
41
+
42
+ The Fixably API does not allow statuses to be destroyed
data/docs/user.md ADDED
@@ -0,0 +1,23 @@
1
+ # Fixably::User
2
+
3
+ ## List users
4
+
5
+ The Fixably API does not allow users to be searched
6
+
7
+ ## Get a user
8
+
9
+ ```ruby
10
+ Fixably::User.find(1_000)
11
+ ```
12
+
13
+ ## Create a user
14
+
15
+ The Fixably API does not allow users to be created
16
+
17
+ ## Update a user
18
+
19
+ The Fixably API does not allow users to be updated
20
+
21
+ ## Destroy a user
22
+
23
+ The Fixably API does not allow users to be destroyed
data/fixably.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/fixably/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fixably"
7
+ spec.version = Fixably::VERSION
8
+ spec.authors = ["Adam Rice"]
9
+ spec.email = ["development@hashnotadam.com"]
10
+
11
+ spec.summary = "Ruby client for the Fixably API"
12
+ spec.homepage = "https://github.com/HashNotAdam/fixably-ruby"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "activeresource", "~> 5"
29
+
30
+ spec.add_development_dependency "mutant-rspec", "~> 0.10"
31
+ spec.add_development_dependency "pry-byebug"
32
+ spec.add_development_dependency "rake", "~> 13.0"
33
+ spec.add_development_dependency "rspec", "~> 3"
34
+ spec.add_development_dependency "rubocop", "~> 1"
35
+ spec.add_development_dependency "rubocop-performance"
36
+ spec.add_development_dependency "rubocop-rake"
37
+ spec.add_development_dependency "rubocop-rspec"
38
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fixably
4
+ class ActionPolicy
5
+ attr_reader :resource
6
+
7
+ def initialize(resource:)
8
+ @resource = resource.instance_of?(Class) ? resource : resource.class
9
+ validate_resource!
10
+ end
11
+
12
+ def create?
13
+ resource.actions.include?(:create)
14
+ end
15
+
16
+ def create!
17
+ return true if create?
18
+
19
+ raise(
20
+ UnsupportedError,
21
+ "Fixably does not support creating #{resource_name}"
22
+ )
23
+ end
24
+
25
+ def delete?
26
+ resource.actions.include?(:delete)
27
+ end
28
+
29
+ def delete!
30
+ return true if delete?
31
+
32
+ raise(
33
+ UnsupportedError,
34
+ "Fixably does not support deleting #{resource_name}"
35
+ )
36
+ end
37
+
38
+ def list?
39
+ resource.actions.include?(:list)
40
+ end
41
+
42
+ def list!
43
+ return true if list?
44
+
45
+ raise(
46
+ UnsupportedError,
47
+ "Fixably does not support listing #{resource_name}"
48
+ )
49
+ end
50
+
51
+ def show?
52
+ resource.actions.include?(:show)
53
+ end
54
+
55
+ def show!
56
+ return true if show?
57
+
58
+ raise(
59
+ UnsupportedError,
60
+ "Fixably does not support retrieving #{resource_name}"
61
+ )
62
+ end
63
+
64
+ def update?
65
+ resource.actions.include?(:update)
66
+ end
67
+
68
+ def update!
69
+ return true if update?
70
+
71
+ raise(
72
+ UnsupportedError,
73
+ "Fixably does not support updating #{resource_name}"
74
+ )
75
+ end
76
+
77
+ private
78
+
79
+ def validate_resource!
80
+ return if resource.ancestors.include?(ApplicationResource)
81
+
82
+ raise(
83
+ ArgumentError,
84
+ "The resource should inherit from ApplicationResource"
85
+ )
86
+ end
87
+
88
+ def resource_name
89
+ resource.name.split("::").last.underscore.humanize.pluralize.downcase
90
+ end
91
+ end
92
+
93
+ class UnsupportedError < StandardError; end
94
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "action_policy"
4
+ require_relative "argument_parameterisation"
5
+ require_relative "resource_lazy_loader"
6
+
7
+ module Fixably
8
+ module Actions
9
+ def self.included(klass)
10
+ klass.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ include ArgumentParameterisation
15
+
16
+ def actions(values = nil)
17
+ if eql?(ApplicationResource)
18
+ raise "actions can only be called on a sub-class"
19
+ end
20
+
21
+ @actions ||= [].freeze
22
+ return @actions if values.nil?
23
+
24
+ @actions = format_actions(values).freeze
25
+ end
26
+
27
+ def all(*arguments)
28
+ ActionPolicy.new(resource: self).list!
29
+ super(*arguments)
30
+ end
31
+
32
+ def create(attributes = {})
33
+ ActionPolicy.new(resource: self).create!
34
+ super(attributes)
35
+ end
36
+
37
+ def create!(attributes = {})
38
+ ActionPolicy.new(resource: self).create!
39
+ super(attributes)
40
+ end
41
+
42
+ def delete(id, options = {})
43
+ ActionPolicy.new(resource: self).delete!
44
+ super(id, options)
45
+ end
46
+
47
+ def exists?(id, options = {})
48
+ find(id, options)
49
+ true
50
+ rescue ::ActiveResource::ResourceNotFound
51
+ false
52
+ end
53
+
54
+ def find(*arguments)
55
+ scope = arguments.slice(0)
56
+
57
+ ActionPolicy.new(resource: self).show! unless scope.instance_of?(Symbol)
58
+
59
+ args = parametize_arguments(scope, arguments.slice(1))
60
+
61
+ super(scope, args)
62
+ end
63
+
64
+ def first(*arguments)
65
+ ActionPolicy.new(resource: self).list!
66
+
67
+ args = arguments.first || {}
68
+ args[:limit] = 1
69
+ super(args)
70
+ end
71
+
72
+ def includes(association)
73
+ ResourceLazyLoader.new(model: self).includes(association)
74
+ end
75
+
76
+ def last(*arguments)
77
+ ActionPolicy.new(resource: self).list!
78
+
79
+ args = parametize_arguments(:last, arguments.first)
80
+ collection = find_every(args)
81
+ return collection.last unless collection.offset.zero?
82
+ return collection.last if collection.total_items <= collection.limit
83
+
84
+ args = args[:params].merge(limit: 1, offset: collection.total_items - 1)
85
+ super(args)
86
+ end
87
+
88
+ def where(clauses = {})
89
+ ActionPolicy.new(resource: self).list!
90
+
91
+ arguments = stringify_array_values(clauses)
92
+ find(:all, arguments)
93
+ end
94
+
95
+ private
96
+
97
+ # rubocop:disable Metrics/MethodLength
98
+ def format_actions(values)
99
+ unless values.respond_to?(:to_sym) || values.respond_to?(:to_a)
100
+ raise(
101
+ ArgumentError,
102
+ "actions should be able to be converted into an Array or a Symbol"
103
+ )
104
+ end
105
+
106
+ Array.wrap(values).map do
107
+ action = _1.to_sym
108
+
109
+ unless allowed_actions.include?(action)
110
+ raise ArgumentError, "Unsupported action, #{action}, supplied"
111
+ end
112
+
113
+ action
114
+ end
115
+ end
116
+ # rubocop:enable Metrics/MethodLength
117
+
118
+ def allowed_actions = %i[create delete list show update]
119
+ end
120
+
121
+ def destroy
122
+ ActionPolicy.new(resource: self).delete!
123
+ super()
124
+ end
125
+
126
+ def save(validate: true)
127
+ if validate
128
+ message = new? ? :create! : :update!
129
+ ActionPolicy.new(resource: self).public_send(message)
130
+ end
131
+
132
+ super()
133
+ end
134
+
135
+ # rubocop:disable Style/RaiseArgs
136
+ def save!
137
+ if new?
138
+ ActionPolicy.new(resource: self).create!
139
+ else
140
+ ActionPolicy.new(resource: self).update!
141
+ end
142
+
143
+ save(validate: false) ||
144
+ raise(::ActiveResource::ResourceInvalid.new(self))
145
+ end
146
+ # rubocop:enable Style/RaiseArgs
147
+ end
148
+ end