gecko-ruby 0.0.4

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 (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +9 -0
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +41 -0
  6. data/CONTRIBUTING.md +0 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +144 -0
  10. data/Rakefile +19 -0
  11. data/gecko-ruby.gemspec +34 -0
  12. data/generate.thor +81 -0
  13. data/lib/gecko-ruby.rb +1 -0
  14. data/lib/gecko.rb +31 -0
  15. data/lib/gecko/client.rb +132 -0
  16. data/lib/gecko/helpers/association_helper.rb +69 -0
  17. data/lib/gecko/helpers/inspection_helper.rb +51 -0
  18. data/lib/gecko/helpers/record_helper.rb +29 -0
  19. data/lib/gecko/helpers/serialization_helper.rb +61 -0
  20. data/lib/gecko/helpers/validation_helper.rb +91 -0
  21. data/lib/gecko/record/account.rb +66 -0
  22. data/lib/gecko/record/address.rb +33 -0
  23. data/lib/gecko/record/base.rb +66 -0
  24. data/lib/gecko/record/base_adapter.rb +365 -0
  25. data/lib/gecko/record/company.rb +41 -0
  26. data/lib/gecko/record/contact.rb +25 -0
  27. data/lib/gecko/record/currency.rb +23 -0
  28. data/lib/gecko/record/exceptions.rb +15 -0
  29. data/lib/gecko/record/fulfillment.rb +42 -0
  30. data/lib/gecko/record/fulfillment_line_item.rb +26 -0
  31. data/lib/gecko/record/image.rb +38 -0
  32. data/lib/gecko/record/invoice.rb +29 -0
  33. data/lib/gecko/record/invoice_line_item.rb +18 -0
  34. data/lib/gecko/record/location.rb +23 -0
  35. data/lib/gecko/record/order.rb +50 -0
  36. data/lib/gecko/record/order_line_item.rb +35 -0
  37. data/lib/gecko/record/product.rb +27 -0
  38. data/lib/gecko/record/purchase_order.rb +43 -0
  39. data/lib/gecko/record/purchase_order_line_item.rb +29 -0
  40. data/lib/gecko/record/tax_type.rb +21 -0
  41. data/lib/gecko/record/user.rb +44 -0
  42. data/lib/gecko/record/variant.rb +96 -0
  43. data/lib/gecko/version.rb +3 -0
  44. data/test/client_test.rb +29 -0
  45. data/test/fixtures/vcr_cassettes/accounts.yml +57 -0
  46. data/test/fixtures/vcr_cassettes/accounts_current.yml +57 -0
  47. data/test/fixtures/vcr_cassettes/addresses.yml +68 -0
  48. data/test/fixtures/vcr_cassettes/addresses_count.yml +58 -0
  49. data/test/fixtures/vcr_cassettes/companies.yml +62 -0
  50. data/test/fixtures/vcr_cassettes/companies_count.yml +58 -0
  51. data/test/fixtures/vcr_cassettes/contacts.yml +60 -0
  52. data/test/fixtures/vcr_cassettes/contacts_count.yml +58 -0
  53. data/test/fixtures/vcr_cassettes/currencies.yml +62 -0
  54. data/test/fixtures/vcr_cassettes/currencies_count.yml +58 -0
  55. data/test/fixtures/vcr_cassettes/fulfillments.yml +59 -0
  56. data/test/fixtures/vcr_cassettes/fulfillments_count.yml +58 -0
  57. data/test/fixtures/vcr_cassettes/images.yml +59 -0
  58. data/test/fixtures/vcr_cassettes/images_count.yml +58 -0
  59. data/test/fixtures/vcr_cassettes/invoice_line_items.yml +63 -0
  60. data/test/fixtures/vcr_cassettes/invoice_line_items_count.yml +62 -0
  61. data/test/fixtures/vcr_cassettes/invoices.yml +63 -0
  62. data/test/fixtures/vcr_cassettes/invoices_count.yml +62 -0
  63. data/test/fixtures/vcr_cassettes/locations.yml +65 -0
  64. data/test/fixtures/vcr_cassettes/locations_count.yml +58 -0
  65. data/test/fixtures/vcr_cassettes/order_line_items.yml +63 -0
  66. data/test/fixtures/vcr_cassettes/order_line_items_count.yml +62 -0
  67. data/test/fixtures/vcr_cassettes/orders.yml +62 -0
  68. data/test/fixtures/vcr_cassettes/orders_count.yml +58 -0
  69. data/test/fixtures/vcr_cassettes/products.yml +79 -0
  70. data/test/fixtures/vcr_cassettes/products_count.yml +58 -0
  71. data/test/fixtures/vcr_cassettes/products_new_invalid.yml +54 -0
  72. data/test/fixtures/vcr_cassettes/products_new_valid.yml +58 -0
  73. data/test/fixtures/vcr_cassettes/purchase_order_line_items.yml +64 -0
  74. data/test/fixtures/vcr_cassettes/purchase_order_line_items_count.yml +62 -0
  75. data/test/fixtures/vcr_cassettes/purchase_orders.yml +63 -0
  76. data/test/fixtures/vcr_cassettes/purchase_orders_count.yml +62 -0
  77. data/test/fixtures/vcr_cassettes/tax_types.yml +74 -0
  78. data/test/fixtures/vcr_cassettes/tax_types_count.yml +62 -0
  79. data/test/fixtures/vcr_cassettes/users.yml +60 -0
  80. data/test/fixtures/vcr_cassettes/users_count.yml +58 -0
  81. data/test/fixtures/vcr_cassettes/users_current.yml +54 -0
  82. data/test/fixtures/vcr_cassettes/variants.yml +60 -0
  83. data/test/fixtures/vcr_cassettes/variants_count.yml +58 -0
  84. data/test/gecko_test.rb +7 -0
  85. data/test/helpers/association_helper_test.rb +56 -0
  86. data/test/helpers/inspection_helper_test.rb +27 -0
  87. data/test/helpers/serialization_helper_test.rb +30 -0
  88. data/test/helpers/validation_helper_test.rb +24 -0
  89. data/test/record/account_adapter_test.rb +43 -0
  90. data/test/record/address_adapter_test.rb +14 -0
  91. data/test/record/address_test.rb +18 -0
  92. data/test/record/company_adapter_test.rb +14 -0
  93. data/test/record/company_test.rb +18 -0
  94. data/test/record/contact_adapter_test.rb +14 -0
  95. data/test/record/contact_test.rb +18 -0
  96. data/test/record/currency_adapter_test.rb +14 -0
  97. data/test/record/currency_test.rb +18 -0
  98. data/test/record/fulfillment_adapter_test.rb +24 -0
  99. data/test/record/fulfillment_line_item_adapter_test.rb +21 -0
  100. data/test/record/fulfillment_line_item_test.rb +18 -0
  101. data/test/record/fulfillment_test.rb +27 -0
  102. data/test/record/image_adapter_test.rb +14 -0
  103. data/test/record/image_test.rb +25 -0
  104. data/test/record/invoice_adapter_test.rb +14 -0
  105. data/test/record/invoice_line_item_adapter_test.rb +20 -0
  106. data/test/record/invoice_line_item_test.rb +18 -0
  107. data/test/record/invoice_test.rb +18 -0
  108. data/test/record/location_adapter_test.rb +14 -0
  109. data/test/record/location_test.rb +18 -0
  110. data/test/record/order_adapter_test.rb +14 -0
  111. data/test/record/order_line_item_adapter_test.rb +14 -0
  112. data/test/record/order_line_item_test.rb +18 -0
  113. data/test/record/order_test.rb +18 -0
  114. data/test/record/product_adapter_test.rb +32 -0
  115. data/test/record/product_test.rb +18 -0
  116. data/test/record/purchase_order_adapter_test.rb +14 -0
  117. data/test/record/purchase_order_line_item_adapter_test.rb +14 -0
  118. data/test/record/purchase_order_line_item_test.rb +18 -0
  119. data/test/record/purchase_order_test.rb +18 -0
  120. data/test/record/tax_type_adapter_test.rb +14 -0
  121. data/test/record/tax_type_test.rb +18 -0
  122. data/test/record/user_adapter_test.rb +27 -0
  123. data/test/record/user_test.rb +18 -0
  124. data/test/record/variant_adapter_test.rb +14 -0
  125. data/test/record/variant_test.rb +44 -0
  126. data/test/support/let.rb +10 -0
  127. data/test/support/shared_adapter_examples.rb +159 -0
  128. data/test/support/shared_record_examples.rb +21 -0
  129. data/test/support/testing_adapter.rb +11 -0
  130. data/test/support/vcr_support.rb +7 -0
  131. data/test/test_helper.rb +21 -0
  132. metadata +430 -0
@@ -0,0 +1,69 @@
1
+ module Gecko
2
+ module Helpers
3
+ # Helper for has_many/belongs_to relationships
4
+ module AssociationHelper
5
+ # Set up a belongs_to relationship between two classes based on a
6
+ # JSON key of {class_name}_id.
7
+ #
8
+ # @example
9
+ # class Gecko::Record::Variant
10
+ # belongs_to :product
11
+ # end
12
+ #
13
+ # @param [Symbol] model_name
14
+ # @param [#to_hash] options options for setting up the relationship
15
+ # @option options [String] :class_name Override the default class name
16
+ # @option options [String] :readonly (false) Whether to serialize
17
+ # this attribute
18
+ #
19
+ # @return [undefined]
20
+ #
21
+ # @api public
22
+ def belongs_to(model_name, options={})
23
+ class_name = options[:class_name] || model_name.to_s.classify
24
+ foreign_key = model_name.to_s.foreign_key.to_sym
25
+
26
+ define_method model_name do
27
+ if (id = attributes[foreign_key])
28
+ @client.adapter_for(class_name).find(id)
29
+ end
30
+ end
31
+ attribute foreign_key, Integer, readonly: options[:readonly]
32
+ end
33
+
34
+ # Set up a has_many relationship between two classes based on a
35
+ # JSON key of {class_name}_ids.
36
+ #
37
+ # @example
38
+ # class Gecko::Record::Product
39
+ # has_many :variants
40
+ # end
41
+ #
42
+ # @param [Symbol] model_name
43
+ # @param [#to_hash] options options for setting up the relationship
44
+ # @option options [String] :class_name Override the default class name
45
+ # @option options [String] :readonly (true) Whether to serialize this
46
+ # attribute
47
+ #
48
+ # @return [undefined]
49
+ #
50
+ # @api public
51
+ def has_many(association_name, options={})
52
+ model_name = association_name.to_s.singularize
53
+ class_name = options[:class_name] || model_name.classify
54
+ foreign_key = model_name.foreign_key.pluralize.to_sym
55
+ readonly = options.key?(:readonly) ? options[:readonly] : true
56
+
57
+ define_method association_name do
58
+ ids = self.attributes[foreign_key]
59
+ if ids.any?
60
+ @client.adapter_for(class_name).find_many(ids)
61
+ else
62
+ []
63
+ end
64
+ end
65
+ attribute foreign_key, Array[Integer], readonly: readonly
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,51 @@
1
+ module Gecko
2
+ module Helpers
3
+ # Helper for providing custom #inspect values to Records
4
+ module InspectionHelper
5
+ # Overrides the default inspect to just return the defined attributes
6
+ #
7
+ # @example
8
+ # company.inspect #=> <Gecko::Record::Company id=123 name="Gecko Inc">
9
+ #
10
+ # @return [String]
11
+ #
12
+ # @api public
13
+ def inspect
14
+ inspection = self.class.attribute_set.map do |attribute|
15
+ name = attribute.name
16
+ "#{name}: #{attribute_for_inspect(name)}"
17
+ end.join(', ')
18
+ "#<#{self.class} #{inspection}>"
19
+ end
20
+
21
+ private
22
+
23
+ # Returns an #inspect-like string for the value of the attribute
24
+ # attr_name.
25
+ # String attributes are truncated up to 50 characters,
26
+ # and Date and Time attributes are returned in the :db format.
27
+ # Other attributes return the value of #inspect without modification.
28
+ # Duplicated from Rails implementation
29
+ #
30
+ # @example
31
+ # company.inspect #=> <Gecko::Record::Company id=123 name="Gecko Inc">
32
+ #
33
+ # @return [String]
34
+ #
35
+ # @api private
36
+ def attribute_for_inspect(attr_name)
37
+ value = attributes[attr_name]
38
+
39
+ if value.is_a?(String) && value.length > 50
40
+ "#{value[0..50]}...".inspect
41
+ elsif value.is_a?(DateTime) || value.is_a?(Time)
42
+ %("#{value.strftime("%Y-%m-%d %H:%M:%S")}")
43
+ elsif value.is_a?(Date)
44
+ %("#{value.strftime("%Y-%m-%d")}")
45
+ else
46
+ value.inspect
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module Gecko
2
+ module Helpers
3
+ # Helper for registering valid record types
4
+ module RecordHelper
5
+ # Registers a record type on the Gecko::Client
6
+ #
7
+ # @example
8
+ # class Gecko::Client
9
+ # record :Invoice
10
+ # end
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ def record(record_type)
16
+ define_method record_type do
17
+ adapter_cache = "@#{record_type}_cache".to_sym
18
+ unless instance_variable_defined?(adapter_cache)
19
+ adapter_name = "#{record_type}Adapter".to_sym
20
+ adapter_klass = Gecko::Record.const_get(adapter_name)
21
+ adapter = adapter_klass.new(self, record_type)
22
+ instance_variable_set(adapter_cache, adapter)
23
+ end
24
+ instance_variable_get(adapter_cache)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,61 @@
1
+ module Gecko
2
+ module Helpers
3
+ # Provides serialization to records
4
+ module SerializationHelper
5
+ # Returns a full JSON representation of a record
6
+ #
7
+ # @example
8
+ # product.as_json #=> {product: {id: 12, name: "Big"}}
9
+ #
10
+ # @return [Hash]
11
+ #
12
+ # @api private
13
+ def as_json
14
+ {
15
+ root => serializable_hash
16
+ }
17
+ end
18
+
19
+ # Return a serialized hash of the record's attributes
20
+ #
21
+ # @example
22
+ # product.serializable_hash #=> {id: 12, name: "Big"}
23
+ #
24
+ # @return [Hash]
25
+ #
26
+ # @api private
27
+ def serializable_hash
28
+ attribute_hash = {}
29
+ attribute_set.each do |attribute|
30
+ next if attribute.options[:readonly]
31
+ serialize_attribute(attribute_hash, attribute)
32
+ end
33
+ attribute_hash
34
+ end
35
+
36
+ # Serialize a single attribute
37
+ #
38
+ # @param [Hash] attribute_hash Serialized record being iterated over
39
+ # @param [Virtus::Attribute] attribute The attribute being serialized
40
+ #
41
+ # @return [undefined]
42
+ #
43
+ # @api private
44
+ def serialize_attribute(attribute_hash, attribute)
45
+ attribute_hash[attribute.name] = attributes[attribute.name]
46
+ end
47
+
48
+ # Return JSON root key for a record
49
+ #
50
+ # @example
51
+ # product.root #=> "product"
52
+ #
53
+ # @return [String]
54
+ #
55
+ # @api private
56
+ def root
57
+ self.class.demodulized_name.underscore.to_sym
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,91 @@
1
+ module Gecko
2
+ module Helpers
3
+ # Provides record validation
4
+ module ValidationHelper
5
+ # Returns whether a record is considered valid or not
6
+ #
7
+ # @return [Boolean]
8
+ #
9
+ # @api public
10
+ def valid?
11
+ errors.empty?
12
+ end
13
+
14
+ # Returns the validation errors object for the record
15
+ #
16
+ # @return [Gecko::Errors]
17
+ #
18
+ # @api public
19
+ def errors
20
+ @errors ||= Gecko::Errors.new(self)
21
+ end
22
+ end
23
+ end
24
+
25
+ class Errors
26
+ # The hash of errors for this record
27
+ #
28
+ # @return [Hash]
29
+ #
30
+ # @api public
31
+ attr_reader :messages
32
+
33
+ # Set up the error object
34
+ #
35
+ # @api private
36
+ def initialize(base)
37
+ @base = base
38
+ @messages = {}
39
+ end
40
+
41
+ # Fetch the errors for a specific attribute
42
+ #
43
+ # @example
44
+ # product.errors[:name]
45
+ # #=> ["can't be blank"]
46
+ #
47
+ # @params [Symbol] :attribute
48
+ #
49
+ # @return [Array]
50
+ #
51
+ # @api public
52
+ def [](attribute)
53
+ messages[attribute.to_sym]
54
+ end
55
+
56
+ # Clear the validation errors
57
+ #
58
+ # @example
59
+ # product.errors.clear
60
+ #
61
+ # @return [undefined]
62
+ #
63
+ # @api public
64
+ def clear
65
+ @messages.clear
66
+ end
67
+
68
+ # Whether there are any errors
69
+ #
70
+ # @return [Boolean]
71
+ #
72
+ # @api public
73
+ def empty?
74
+ messages.all? { |k, v| v && v.empty? && !v.is_a?(String) }
75
+ end
76
+
77
+ # Parse JSON errors response into the error object
78
+ #
79
+ # @params [#to_hash] :error_hash hash of errors where key is an attribute
80
+ # name and value is an array of error strings
81
+ #
82
+ # @return [undefined]
83
+ #
84
+ # @api private
85
+ def from_response(error_hash)
86
+ error_hash.each do |attr, errors|
87
+ @messages[attr.to_sym] = errors
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,66 @@
1
+ require 'gecko/record/base'
2
+
3
+ module Gecko
4
+ module Record
5
+ class Account < Base
6
+ has_many :users
7
+ has_many :locations
8
+
9
+ attribute :name, String
10
+ attribute :industry, String
11
+ attribute :logo_url, String, readonly: true
12
+ attribute :website, String
13
+ attribute :tax_number, String
14
+ attribute :country, String
15
+ attribute :time_zone, String
16
+
17
+ attribute :contact_email, String
18
+ attribute :contact_mobile, String
19
+ attribute :contact_phone, String
20
+
21
+ attribute :invoice_details, String
22
+ attribute :order_details, String
23
+ attribute :quote_details, String
24
+
25
+ attribute :default_tax_rate, String
26
+
27
+ attribute :default_tax_type_id, Integer
28
+
29
+ attribute :tax_number_label, String
30
+ attribute :tax_label, String
31
+
32
+ belongs_to :billing_contact, class_name: "User"
33
+ belongs_to :primary_location, class_name: "Location"
34
+ belongs_to :primary_billing_location, class_name: "Location"
35
+
36
+ # belongs_to :default_currency, class_name: "Currency"
37
+ # belongs_to :default_payment_term, class_name: "PaymentTerm"
38
+ # belongs_to :default_purchase_order_price_list, class_name: "PriceList"
39
+ # belongs_to :default_order_price_list, class_name: "PriceList"
40
+
41
+ # attribute :stock_level_warn, String
42
+ # attribute :subscription_name, String
43
+ # attribute :subscription_price, String
44
+ # attribute :annual_subscription_price, String
45
+ # attribute :expires_at, String
46
+ end
47
+
48
+ class AccountAdapter < BaseAdapter
49
+ undef :count
50
+ undef :build
51
+
52
+ # Return the account for the logged in user
53
+ #
54
+ # @return [Gecko::Record::Account]
55
+ #
56
+ # @api public
57
+ def current
58
+ if self.has_record_for_id?(:current)
59
+ record_for_id(:current)
60
+ else
61
+ @identity_map[:current] = find(:current)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,33 @@
1
+ require 'gecko/record/base'
2
+
3
+ module Gecko
4
+ module Record
5
+ class Address < Base
6
+ belongs_to :company
7
+
8
+ attribute :phone_number, String
9
+ attribute :email, String
10
+
11
+ attribute :label, String
12
+ attribute :company_name, String
13
+ attribute :address1, String
14
+ attribute :address2, String
15
+ attribute :suburb, String
16
+ attribute :city, String
17
+ attribute :state, String
18
+ attribute :country, String
19
+ attribute :zip_code, String
20
+
21
+ attribute :status, String, readonly: true
22
+
23
+ alias_method :country_code, :country
24
+ end
25
+
26
+ class AddressAdapter < BaseAdapter
27
+ # Override plural_path to properly pluralize address
28
+ def plural_path
29
+ 'addresses'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ require 'gecko/helpers/association_helper'
2
+ require 'gecko/helpers/inspection_helper'
3
+ require 'gecko/helpers/serialization_helper'
4
+ require 'gecko/helpers/validation_helper'
5
+
6
+ module Gecko
7
+ module Record
8
+ class Base
9
+ include Virtus.model
10
+ extend Gecko::Helpers::AssociationHelper
11
+ include Gecko::Helpers::InspectionHelper
12
+ include Gecko::Helpers::SerializationHelper
13
+ include Gecko::Helpers::ValidationHelper
14
+
15
+ # Set up the default attributes associated with all records
16
+ attribute :id, Integer, readonly: true
17
+ attribute :updated_at, DateTime, readonly: true
18
+ attribute :created_at, DateTime, readonly: true
19
+
20
+ # Overrides the default Virtus functionality to store:
21
+ # - The Gecko::Client used to create the object
22
+ # - a raw copy of the attributes for the association helpers to read from
23
+ #
24
+ # @return [undefined]
25
+ #
26
+ # @api private
27
+ def initialize(client, attributes={})
28
+ super(attributes)
29
+ @client = client
30
+ end
31
+
32
+ # Whether the record has been persisted
33
+ #
34
+ # @example
35
+ # variant.persisted? #=> true
36
+ #
37
+ # @return <Boolean>
38
+ #
39
+ # @api public
40
+ def persisted?
41
+ !!id
42
+ end
43
+
44
+ # Save a record
45
+ #
46
+ # @return <Gecko::Record::Base>
47
+ #
48
+ # @api public
49
+ def save
50
+ @client.adapter_for(self.class.demodulized_name).save(self)
51
+ end
52
+
53
+ # Return the demodulized class name
54
+ #
55
+ # @example
56
+ # Gecko::Record::Product.demodulized_name #=> "Product"
57
+ #
58
+ # @return [String]
59
+ #
60
+ # @api private
61
+ def self.demodulized_name
62
+ self.name.split('::').last
63
+ end
64
+ end
65
+ end
66
+ end