fixably 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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