nylas 3.2.0 → 4.0.0.rc2

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.
@@ -0,0 +1,92 @@
1
+ require_relative "model/attribute_definition"
2
+ require_relative "model/list_attribute_definition"
3
+ require_relative "model/attributable"
4
+ require_relative "model/attributes"
5
+ module Nylas
6
+ # Include this to define a class to represent an object returned from the API
7
+ module Model
8
+ attr_accessor :api
9
+
10
+ def self.included(model)
11
+ model.include(Attributable)
12
+ model.extend(ClassMethods)
13
+ model.collectionable = true
14
+ model.searchable = true
15
+ model.read_only = false
16
+ end
17
+
18
+ def save
19
+ raise_if_read_only
20
+ result = if id
21
+ api.execute(method: :put, payload: attributes.serialize, path: resource_path)
22
+ else
23
+ api.execute(method: :post, payload: attributes.serialize, path: resources_path)
24
+ end
25
+ attributes.merge(result)
26
+ end
27
+
28
+ def update(**data)
29
+ raise_if_read_only
30
+ attributes.merge(**data)
31
+ api.execute(method: :put, payload: attributes.serialize(keys: data.keys), path: resource_path)
32
+ true
33
+ end
34
+
35
+ def reload
36
+ attributes.merge(api.execute(method: :get, path: resource_path))
37
+ true
38
+ end
39
+
40
+ def resource_path
41
+ "#{resources_path}/#{id}"
42
+ end
43
+
44
+ def resources_path
45
+ self.class.resources_path
46
+ end
47
+
48
+ def destroy
49
+ api.execute(method: :delete, path: resource_path)
50
+ end
51
+
52
+ # @return [String] JSON String of the model.
53
+ def to_json
54
+ JSON.dump(to_h)
55
+ end
56
+
57
+ def raise_if_read_only
58
+ self.class.raise_if_read_only
59
+ end
60
+
61
+ # Allows you to narrow in exactly what kind of model you're working with
62
+ module ClassMethods
63
+ attr_accessor :resources_path, :searchable, :read_only, :collectionable
64
+
65
+ def read_only?
66
+ read_only == true
67
+ end
68
+
69
+ def raise_if_read_only
70
+ raise NotImplementedError, "#{self} is read only" if read_only?
71
+ end
72
+
73
+ def searchable?
74
+ searchable == true
75
+ end
76
+
77
+ def collectionable?
78
+ collectionable == true
79
+ end
80
+
81
+ def from_json(json, api:)
82
+ from_hash(JSON.parse(json, symbolize_names: true), api: api)
83
+ end
84
+
85
+ def from_hash(data, api:)
86
+ instance = new(**data)
87
+ instance.api = api
88
+ instance
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,57 @@
1
+ module Nylas
2
+ module Model
3
+ # Allows defining of tyypecastable attributes on a model
4
+ module Attributable
5
+ def self.included(model)
6
+ model.extend(ClassMethods)
7
+ end
8
+
9
+ def initialize(**initial_data)
10
+ initial_data.each do |attribute_name, value|
11
+ send(:"#{attribute_name}=", value)
12
+ end
13
+ end
14
+
15
+ def attributes
16
+ @attributes ||= Attributes.new(self.class.attribute_definitions)
17
+ end
18
+
19
+ # @return [Hash] Representation of the model with values serialized into primitives based on their Type
20
+ def to_h
21
+ attributes.to_h
22
+ end
23
+
24
+ # Methods to call when tweaking Attributable classes
25
+ module ClassMethods
26
+ # rubocop:disable Style/PredicateName
27
+ def has_n_of_attribute(name, type_name, exclude_when: [], default: [])
28
+ attribute_definitions[name] = ListAttributeDefinition.new(type_name: type_name,
29
+ exclude_when: exclude_when,
30
+ default: default)
31
+ define_accessors(name)
32
+ end
33
+ # rubocop:enable Style/PredicateName
34
+
35
+ def attribute(name, type_name, exclude_when: [], default: nil)
36
+ attribute_definitions[name] = AttributeDefinition.new(type_name: type_name,
37
+ exclude_when: exclude_when, default: default)
38
+ define_accessors(name)
39
+ end
40
+
41
+ def define_accessors(name)
42
+ define_method :"#{name}" do
43
+ attributes[name]
44
+ end
45
+
46
+ define_method :"#{name}=" do |value|
47
+ attributes[name] = value
48
+ end
49
+ end
50
+
51
+ def attribute_definitions
52
+ @attribute_definitions ||= Registry.new
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module Nylas
2
+ module Model
3
+ # Define a particular attribute for a given model
4
+ class AttributeDefinition
5
+ extend Forwardable
6
+ def_delegators :type, :cast, :serialize
7
+ attr_accessor :type_name, :exclude_when, :default
8
+ def initialize(type_name:, exclude_when:, default:)
9
+ self.type_name = type_name
10
+ self.exclude_when = exclude_when
11
+ self.default = default
12
+ end
13
+
14
+ private def type
15
+ Types.registry[type_name]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ module Nylas
2
+ module Model
3
+ # Stores the actual model data to allow for type casting and clean/dirty checking
4
+ class Attributes
5
+ attr_accessor :data, :attribute_definitions
6
+
7
+ def initialize(attribute_definitions)
8
+ @attribute_definitions = attribute_definitions
9
+ @data = Registry.new(default_attributes)
10
+ end
11
+
12
+ def [](key)
13
+ data[key]
14
+ end
15
+
16
+ def []=(key, value)
17
+ data[key] = cast(key, value)
18
+ end
19
+
20
+ private def cast(key, value)
21
+ attribute_definitions[key].cast(value)
22
+ end
23
+
24
+ # Merges data into the registry while casting input types correctly
25
+ def merge(new_data)
26
+ new_data.each do |attribute_name, value|
27
+ self[attribute_name] = value
28
+ end
29
+ end
30
+
31
+ def to_h(keys: attribute_definitions.keys)
32
+ keys.each_with_object({}) do |key, casted_data|
33
+ value = attribute_definitions[key].serialize(self[key])
34
+ casted_data[key] = value unless value.nil? || value.empty?
35
+ end
36
+ end
37
+
38
+ def serialize(keys: attribute_definitions.keys)
39
+ JSON.dump(to_h(keys: keys))
40
+ end
41
+
42
+ private def default_attributes
43
+ attribute_definitions.keys.zip([]).to_h
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ module Nylas
2
+ module Model
3
+ # Allows models to have an attribute which is a lists of another type of thing
4
+ class ListAttributeDefinition
5
+ attr_accessor :type_name, :exclude_when, :default
6
+
7
+ def initialize(type_name:, exclude_when:, default:)
8
+ self.type_name = type_name
9
+ self.exclude_when = exclude_when
10
+ self.default = default
11
+ end
12
+
13
+ def cast(list)
14
+ return default if list.nil? || list.empty?
15
+ list.map { |item| type.cast(item) }
16
+ end
17
+
18
+ def serialize(list)
19
+ list = default if list.nil? || list.empty?
20
+ list.map { |item| type.serialize(item) }
21
+ end
22
+
23
+ def type
24
+ Types.registry[type_name]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Nylas
2
+ # Structure to represent Nylas's more complex Date Schema
3
+ # @see https://docs.nylas.com/reference#contactsid
4
+ class NylasDate
5
+ extend Forwardable
6
+ def_delegators :date, :===, :==, :<=>, :eql?, :equal?
7
+
8
+ include Model::Attributable
9
+ attribute :object, :string
10
+ attribute :date, :date
11
+ end
12
+
13
+ # Serializes, Deserializes between {NylasDate} objects and a {Hash}
14
+ class NylasDateType < Types::HashType
15
+ casts_to NylasDate
16
+ def cast(value)
17
+ value.is_a?(String) ? super({ object: "date", date: value }) : super
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module Nylas
2
+ # Structure to represent the Phone Number Schema
3
+ # @see https://docs.nylas.com/reference#contactsid
4
+ class PhoneNumber
5
+ include Model::Attributable
6
+ attribute :type, :string
7
+ attribute :number, :string
8
+ end
9
+
10
+ # Serializes, Deserializes between {PhoneNumber} objects and a {Hash}
11
+ class PhoneNumberType < Types::HashType
12
+ casts_to PhoneNumber
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module Nylas
2
+ # Structure to represent the Physical Address schema
3
+ # @see https://docs.nylas.com/reference#contactsid
4
+ class PhysicalAddress
5
+ include Model::Attributable
6
+ attribute :format, :string
7
+ attribute :type, :string
8
+ attribute :street_address, :string
9
+ attribute :postal_code, :string
10
+ attribute :state, :string
11
+ attribute :city, :string
12
+ attribute :country, :string
13
+ end
14
+
15
+ # Serializes, Deserializes between {PhysicalAddress} objects and a {Hash}
16
+ class PhysicalAddressType < Types::HashType
17
+ casts_to PhysicalAddress
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ module Nylas
2
+ # Used to create a hash-like structure which defaults to raising an exception in the event the key to
3
+ # retrieve does not exist.
4
+ class Registry
5
+ # Used to indicate an attempt to retrieve something not yet registered in a registry
6
+ # Includes the list of keys in the registry for debug purposes.
7
+ class MissingKeyError < Error
8
+ def initialize(key, keys)
9
+ super("key #{key} not in #{keys}")
10
+ end
11
+ end
12
+ attr_accessor :registry_data
13
+
14
+ extend Forwardable
15
+ def_delegators :registry_data, :keys, :each, :reduce
16
+
17
+ def initialize(initial_data = {})
18
+ self.registry_data = initial_data.each.each_with_object({}) do |(key, value), registry|
19
+ registry[key] = value
20
+ end
21
+ end
22
+
23
+ def [](key)
24
+ registry_data.fetch(key)
25
+ rescue KeyError
26
+ raise MissingKeyError.new(key, keys)
27
+ end
28
+
29
+ def []=(key, value)
30
+ registry_data[key] = value
31
+ end
32
+
33
+ def to_h
34
+ registry_data
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,83 @@
1
+ module Nylas
2
+ # Collection of attribute types
3
+ module Types
4
+ def self.registry
5
+ @registry ||= Registry.new
6
+ end
7
+
8
+ # Type for attributes that are persisted in the API as a hash but exposed in ruby as a particular
9
+ # structure
10
+ class HashType
11
+ def serialize(object)
12
+ object.to_h
13
+ end
14
+
15
+ def self.casts_to(model)
16
+ @casts_to_model = model
17
+ end
18
+
19
+ class << self
20
+ attr_reader :casts_to_model
21
+ end
22
+
23
+ def model
24
+ self.class.casts_to_model
25
+ end
26
+
27
+ def cast(value)
28
+ return model.new if value.nil?
29
+ return value if already_cast?(value)
30
+ return model.new(**actual_attributes(value)) if value.respond_to?(:key?)
31
+ raise TypeError, "Unable to cast #{value} to a #{model}"
32
+ end
33
+
34
+ def already_cast?(value)
35
+ model.attribute_definitions.keys.all? { |attribute_name| value.respond_to?(attribute_name) }
36
+ end
37
+
38
+ def actual_attributes(hash)
39
+ model.attribute_definitions.keys.each_with_object({}) do |attribute_name, attributes|
40
+ attributes[attribute_name] = hash[attribute_name]
41
+ end
42
+ end
43
+ end
44
+
45
+ # Type for attributes that do not require casting/serializing/deserializing.
46
+ class ValueType
47
+ def cast(object)
48
+ object
49
+ end
50
+
51
+ def serialize(object)
52
+ object
53
+ end
54
+
55
+ def deseralize(object)
56
+ object
57
+ end
58
+ end
59
+
60
+ # Type for attributes represented as an iso8601 dates in the API and Date in Ruby
61
+ class DateType < ValueType
62
+ def cast(value)
63
+ return nil if value.nil?
64
+ Date.parse(value)
65
+ end
66
+
67
+ def serialize(value)
68
+ return value.iso8601 if value.respond_to?(:iso8601)
69
+ value
70
+ end
71
+ end
72
+ Types.registry[:date] = DateType.new
73
+
74
+ # Type for attributes represented as pure strings both within the API and in Ruby
75
+ class StringType < ValueType
76
+ # @param value [Object] Casts the passed in object to a string using #to_s
77
+ def cast(value)
78
+ value.to_s
79
+ end
80
+ end
81
+ Types.registry[:string] = StringType.new
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ module Nylas
2
+ VERSION = "4.0.0.rc2".freeze
3
+ end
@@ -0,0 +1,14 @@
1
+ module Nylas
2
+ # Structure to represent the Web Page Schema
3
+ # @see https://docs.nylas.com/reference#contactsid
4
+ class WebPage
5
+ include Model::Attributable
6
+ attribute :type, :string
7
+ attribute :url, :string
8
+ end
9
+
10
+ # Serializes, Deserializes between {WebPage} objects and a {Hash}
11
+ class WebPageType < Types::HashType
12
+ casts_to WebPage
13
+ end
14
+ end