attio-ruby 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +57 -12
- data/examples/app_specific_typed_record.md +1613 -0
- data/examples/deals.rb +112 -0
- data/examples/oauth_flow.rb +26 -49
- data/examples/typed_records_example.rb +10 -7
- data/lib/attio/internal/record.rb +17 -8
- data/lib/attio/resources/company.rb +26 -24
- data/lib/attio/resources/deal.rb +288 -0
- data/lib/attio/resources/meta.rb +43 -12
- data/lib/attio/resources/object.rb +24 -4
- data/lib/attio/resources/person.rb +22 -18
- data/lib/attio/resources/typed_record.rb +49 -6
- data/lib/attio/resources/workspace_member.rb +17 -4
- data/lib/attio/version.rb +1 -1
- data/lib/attio.rb +1 -0
- metadata +4 -2
- data/attio-ruby.gemspec +0 -61
@@ -0,0 +1,288 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "typed_record"
|
4
|
+
|
5
|
+
module Attio
|
6
|
+
# Represents a Deal record in Attio
|
7
|
+
#
|
8
|
+
# @example Create a deal
|
9
|
+
# deal = Attio::Deal.create(
|
10
|
+
# name: "Enterprise Deal",
|
11
|
+
# value: 50000,
|
12
|
+
# stage: "In Progress"
|
13
|
+
# )
|
14
|
+
#
|
15
|
+
# @example Find deals by status
|
16
|
+
# open_deals = Attio::Deal.find_by(status: "open")
|
17
|
+
# won_deals = Attio::Deal.find_by(status: "won")
|
18
|
+
#
|
19
|
+
# @example Find high-value deals
|
20
|
+
# big_deals = Attio::Deal.find_by_value_range(min: 100000)
|
21
|
+
#
|
22
|
+
# @example Update deal status
|
23
|
+
# deal.update_status("won")
|
24
|
+
#
|
25
|
+
class Deal < TypedRecord
|
26
|
+
object_type "deals"
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Create a deal with a simplified interface
|
30
|
+
# @param attributes [Hash] Deal attributes
|
31
|
+
# @option attributes [String] :name Deal name (recommended)
|
32
|
+
# @option attributes [Numeric] :value Deal value (recommended)
|
33
|
+
# @option attributes [String] :stage Deal stage (recommended) - defaults: "Lead", "In Progress", "Won 🎉", "Lost"
|
34
|
+
# @option attributes [String] :status Deal status (alias for stage)
|
35
|
+
# @option attributes [String] :owner Owner email or workspace member (recommended)
|
36
|
+
# @option attributes [Array<String>] :associated_people Email addresses of associated people
|
37
|
+
# @option attributes [Array<String>] :associated_company Domains of associated companies
|
38
|
+
# @option attributes [Hash] :values Raw values hash (for advanced use)
|
39
|
+
def create(name:, value: nil, stage: nil, status: nil, owner: nil,
|
40
|
+
associated_people: nil, associated_company: nil, values: {}, **opts)
|
41
|
+
# Name is required and simple
|
42
|
+
values[:name] = name if name && !values[:name]
|
43
|
+
|
44
|
+
# Add optional fields
|
45
|
+
values[:value] = value if value && !values[:value]
|
46
|
+
|
47
|
+
# Handle stage vs status - API uses "stage" but we support both
|
48
|
+
if (stage || status) && !values[:stage]
|
49
|
+
values[:stage] = stage || status
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Handle owner - can be email address or workspace member reference
|
54
|
+
if owner && !values[:owner]
|
55
|
+
values[:owner] = owner
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle associated people - convert email array to proper format
|
59
|
+
if associated_people && !values[:associated_people]
|
60
|
+
values[:associated_people] = associated_people.map do |email|
|
61
|
+
{
|
62
|
+
target_object: "people",
|
63
|
+
email_addresses: [
|
64
|
+
{ email_address: email }
|
65
|
+
]
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Handle associated company - convert domain array to proper format
|
71
|
+
if associated_company && !values[:associated_company]
|
72
|
+
# associated_company can be array of domains or single domain
|
73
|
+
domains = associated_company.is_a?(Array) ? associated_company : [associated_company]
|
74
|
+
values[:associated_company] = {
|
75
|
+
target_object: "companies",
|
76
|
+
domains: domains.map { |domain| { domain: domain } }
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
super(values: values, **opts)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Find deals within a value range
|
84
|
+
# @param min [Numeric] Minimum value (optional)
|
85
|
+
# @param max [Numeric] Maximum value (optional)
|
86
|
+
# @return [Attio::ListObject] List of matching deals
|
87
|
+
def find_by_value_range(min: nil, max: nil, **opts)
|
88
|
+
filters = []
|
89
|
+
filters << {value: {"$gte": min}} if min
|
90
|
+
filters << {value: {"$lte": max}} if max
|
91
|
+
|
92
|
+
filter = if filters.length == 1
|
93
|
+
filters.first
|
94
|
+
elsif filters.length > 1
|
95
|
+
{"$and": filters}
|
96
|
+
else
|
97
|
+
{}
|
98
|
+
end
|
99
|
+
|
100
|
+
list(**opts.merge(params: {filter: filter}))
|
101
|
+
end
|
102
|
+
|
103
|
+
# # Find deals closing soon (requires close_date attribute)
|
104
|
+
# # @param days [Integer] Number of days from today
|
105
|
+
# # @return [Attio::ListObject] List of deals closing soon
|
106
|
+
# def closing_soon(days: 30, **opts)
|
107
|
+
# today = Date.today
|
108
|
+
# end_date = today + days
|
109
|
+
#
|
110
|
+
# list(**opts.merge(params: {
|
111
|
+
# filter: {
|
112
|
+
# "$and": [
|
113
|
+
# {close_date: {"$gte": today.iso8601}},
|
114
|
+
# {close_date: {"$lte": end_date.iso8601}},
|
115
|
+
# {stage: {"$ne": "Won 🎉"}},
|
116
|
+
# {stage: {"$ne": "Lost"}}
|
117
|
+
# ]
|
118
|
+
# }
|
119
|
+
# }))
|
120
|
+
# end
|
121
|
+
|
122
|
+
# Find deals by owner
|
123
|
+
# @param owner_id [String] The workspace member ID
|
124
|
+
# @return [Attio::ListObject] List of deals owned by the member
|
125
|
+
def find_by_owner(owner_id, **opts)
|
126
|
+
list(**opts.merge(params: {
|
127
|
+
filter: {
|
128
|
+
owner: {
|
129
|
+
target_object: "workspace_members",
|
130
|
+
target_record_id: owner_id
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}))
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Build filter for status field (maps to stage)
|
139
|
+
def filter_by_status(value)
|
140
|
+
{ stage: value }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the deal name
|
145
|
+
# @return [String, nil] The deal name
|
146
|
+
def name
|
147
|
+
self[:name]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Get the deal value
|
151
|
+
# @return [Numeric, nil] The deal value
|
152
|
+
def value
|
153
|
+
self[:value]
|
154
|
+
end
|
155
|
+
|
156
|
+
# Get the deal stage (API uses "stage" but we provide status for compatibility)
|
157
|
+
# @return [String, nil] The deal stage
|
158
|
+
def stage
|
159
|
+
self[:stage]
|
160
|
+
end
|
161
|
+
|
162
|
+
# Alias for stage (for compatibility)
|
163
|
+
# @return [String, nil] The deal stage
|
164
|
+
def status
|
165
|
+
self[:stage]
|
166
|
+
end
|
167
|
+
|
168
|
+
# # Get the close date (if attribute exists)
|
169
|
+
# # @return [String, nil] The close date
|
170
|
+
# def close_date
|
171
|
+
# self[:close_date]
|
172
|
+
# end
|
173
|
+
|
174
|
+
# # Get the probability (if attribute exists)
|
175
|
+
# # @return [Numeric, nil] The win probability
|
176
|
+
# def probability
|
177
|
+
# self[:probability]
|
178
|
+
# end
|
179
|
+
|
180
|
+
# Get the owner reference
|
181
|
+
# @return [Hash, nil] The owner reference
|
182
|
+
def owner
|
183
|
+
self[:owner]
|
184
|
+
end
|
185
|
+
|
186
|
+
# Get the company reference
|
187
|
+
# @return [Hash, nil] The company reference
|
188
|
+
def company
|
189
|
+
self[:company]
|
190
|
+
end
|
191
|
+
|
192
|
+
# Update the deal stage
|
193
|
+
# @param new_stage [String] The new stage
|
194
|
+
# @return [Attio::Deal] The updated deal
|
195
|
+
def update_stage(new_stage, **opts)
|
196
|
+
self.class.update(id, values: {stage: new_stage}, **opts)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Alias for update_stage (for compatibility)
|
200
|
+
# @param new_status [String] The new status/stage
|
201
|
+
# @return [Attio::Deal] The updated deal
|
202
|
+
def update_status(new_status, **opts)
|
203
|
+
update_stage(new_status, **opts)
|
204
|
+
end
|
205
|
+
|
206
|
+
# # Update the deal probability (if attribute exists)
|
207
|
+
# # @param new_probability [Numeric] The new probability (0-100)
|
208
|
+
# # @return [Attio::Deal] The updated deal
|
209
|
+
# def update_probability(new_probability, **opts)
|
210
|
+
# self.class.update(id, values: {probability: new_probability}, **opts)
|
211
|
+
# end
|
212
|
+
|
213
|
+
# Update the deal value
|
214
|
+
# @param new_value [Numeric] The new value
|
215
|
+
# @return [Attio::Deal] The updated deal
|
216
|
+
def update_value(new_value, **opts)
|
217
|
+
self.class.update(id, values: {value: new_value}, **opts)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get the associated company record
|
221
|
+
# @return [Attio::Company, nil] The company record if associated
|
222
|
+
def company_record(**opts)
|
223
|
+
return nil unless company
|
224
|
+
|
225
|
+
company_id = company.is_a?(Hash) ? company["target_record_id"] : company
|
226
|
+
Company.retrieve(company_id, **opts) if company_id
|
227
|
+
end
|
228
|
+
|
229
|
+
# Get the owner workspace member record
|
230
|
+
# @return [Attio::WorkspaceMember, nil] The owner record if assigned
|
231
|
+
def owner_record(**opts)
|
232
|
+
return nil unless owner
|
233
|
+
|
234
|
+
owner_id = if owner.is_a?(Hash)
|
235
|
+
owner["referenced_actor_id"] || owner["target_record_id"]
|
236
|
+
else
|
237
|
+
owner
|
238
|
+
end
|
239
|
+
WorkspaceMember.retrieve(owner_id, **opts) if owner_id
|
240
|
+
end
|
241
|
+
|
242
|
+
# # Calculate expected revenue (value * probability / 100)
|
243
|
+
# # @return [Numeric, nil] The expected revenue
|
244
|
+
# def expected_revenue
|
245
|
+
# return nil unless value && probability
|
246
|
+
# (value * probability / 100.0).round(2)
|
247
|
+
# end
|
248
|
+
|
249
|
+
# Check if the deal is open
|
250
|
+
# @return [Boolean] True if the deal is open
|
251
|
+
def open?
|
252
|
+
return false unless stage
|
253
|
+
|
254
|
+
stage_title = stage.is_a?(Hash) ? stage.dig("status", "title") : stage
|
255
|
+
stage_title && !["won 🎉", "lost"].include?(stage_title.downcase)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Check if the deal is won
|
259
|
+
# @return [Boolean] True if the deal is won
|
260
|
+
def won?
|
261
|
+
return false unless stage
|
262
|
+
|
263
|
+
stage_title = stage.is_a?(Hash) ? stage.dig("status", "title") : stage
|
264
|
+
stage_title && stage_title.downcase.include?("won")
|
265
|
+
end
|
266
|
+
|
267
|
+
# Check if the deal is lost
|
268
|
+
# @return [Boolean] True if the deal is lost
|
269
|
+
def lost?
|
270
|
+
return false unless stage
|
271
|
+
|
272
|
+
stage_title = stage.is_a?(Hash) ? stage.dig("status", "title") : stage
|
273
|
+
stage_title && stage_title.downcase == "lost"
|
274
|
+
end
|
275
|
+
|
276
|
+
# # Check if the deal is overdue
|
277
|
+
# # @return [Boolean] True if close date has passed and deal is still open
|
278
|
+
# def overdue?
|
279
|
+
# return false unless close_date && open?
|
280
|
+
# Date.parse(close_date) < Date.today
|
281
|
+
# end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Alias for Deal (plural form)
|
285
|
+
# @example
|
286
|
+
# Attio::Deals.create(name: "New Deal", value: 10000)
|
287
|
+
Deals = Deal
|
288
|
+
end
|
data/lib/attio/resources/meta.rb
CHANGED
@@ -23,47 +23,78 @@ module Attio
|
|
23
23
|
alias_method :current, :identify
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
27
|
-
|
26
|
+
# Build workspace object from flat attributes
|
27
|
+
def workspace
|
28
|
+
return nil unless self[:workspace_id]
|
29
|
+
|
30
|
+
{
|
31
|
+
id: self[:workspace_id],
|
32
|
+
name: self[:workspace_name],
|
33
|
+
slug: self[:workspace_slug],
|
34
|
+
logo_url: self[:workspace_logo_url]
|
35
|
+
}.compact
|
36
|
+
end
|
37
|
+
|
38
|
+
# Build token object from flat attributes
|
39
|
+
def token
|
40
|
+
return nil unless self[:client_id]
|
41
|
+
|
42
|
+
{
|
43
|
+
id: self[:client_id],
|
44
|
+
type: self[:token_type] || "Bearer",
|
45
|
+
scope: self[:scope]
|
46
|
+
}.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
# Build actor object from flat attributes
|
50
|
+
def actor
|
51
|
+
return nil unless self[:authorized_by_workspace_member_id]
|
52
|
+
|
53
|
+
{
|
54
|
+
type: "workspace-member",
|
55
|
+
id: self[:authorized_by_workspace_member_id]
|
56
|
+
}
|
57
|
+
end
|
28
58
|
|
29
59
|
# Convenience methods for workspace info
|
30
60
|
def workspace_id
|
31
|
-
|
61
|
+
self[:workspace_id]
|
32
62
|
end
|
33
63
|
|
34
64
|
# Get the workspace name
|
35
65
|
# @return [String, nil] The workspace name
|
36
66
|
def workspace_name
|
37
|
-
|
67
|
+
self[:workspace_name]
|
38
68
|
end
|
39
69
|
|
40
70
|
# Get the workspace slug
|
41
71
|
# @return [String, nil] The workspace slug
|
42
72
|
def workspace_slug
|
43
|
-
|
73
|
+
self[:workspace_slug]
|
44
74
|
end
|
45
75
|
|
46
76
|
# Convenience methods for token info
|
47
77
|
def token_id
|
48
|
-
|
78
|
+
self[:client_id]
|
49
79
|
end
|
50
80
|
|
51
81
|
# Get the token name
|
52
|
-
# @return [String, nil] The token name
|
82
|
+
# @return [String, nil] The token name (not available in flat format)
|
53
83
|
def token_name
|
54
|
-
|
84
|
+
nil
|
55
85
|
end
|
56
86
|
|
57
87
|
# Get the token type
|
58
88
|
# @return [String, nil] The token type
|
59
89
|
def token_type
|
60
|
-
|
90
|
+
self[:token_type]
|
61
91
|
end
|
62
92
|
|
63
93
|
# Get the token's OAuth scopes
|
64
94
|
# @return [Array<String>] Array of scope strings
|
65
95
|
def scopes
|
66
|
-
|
96
|
+
return [] unless self[:scope]
|
97
|
+
self[:scope].split(" ")
|
67
98
|
end
|
68
99
|
|
69
100
|
# Check if token has a specific scope
|
@@ -96,8 +127,6 @@ module Attio
|
|
96
127
|
raise InvalidRequestError, "Meta information is read-only"
|
97
128
|
end
|
98
129
|
|
99
|
-
private
|
100
|
-
|
101
130
|
def to_h
|
102
131
|
{
|
103
132
|
workspace: workspace,
|
@@ -109,5 +138,7 @@ module Attio
|
|
109
138
|
def inspect
|
110
139
|
"#<#{self.class.name}:#{object_id} workspace=#{workspace_slug.inspect} token=#{token_name.inspect}>"
|
111
140
|
end
|
141
|
+
|
142
|
+
private
|
112
143
|
end
|
113
144
|
end
|
@@ -45,11 +45,31 @@ module Attio
|
|
45
45
|
Internal::Record.create(object: api_slug || id, values: values, **)
|
46
46
|
end
|
47
47
|
|
48
|
-
# Find by
|
48
|
+
# Find by attribute using Rails-style syntax
|
49
|
+
def self.find_by(**conditions)
|
50
|
+
# Extract any opts that aren't conditions
|
51
|
+
opts = {}
|
52
|
+
known_opts = [:api_key, :timeout, :idempotency_key]
|
53
|
+
known_opts.each do |opt|
|
54
|
+
opts[opt] = conditions.delete(opt) if conditions.key?(opt)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Currently only supports slug
|
58
|
+
if conditions.key?(:slug)
|
59
|
+
slug = conditions[:slug]
|
60
|
+
begin
|
61
|
+
retrieve(slug, **opts)
|
62
|
+
rescue NotFoundError
|
63
|
+
list(**opts).find { |obj| obj.api_slug == slug }
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise ArgumentError, "find_by only supports slug attribute for objects"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Find by API slug (deprecated - use find_by(slug: ...) instead)
|
49
71
|
def self.find_by_slug(slug, **opts)
|
50
|
-
|
51
|
-
rescue NotFoundError
|
52
|
-
list(**opts).find { |obj| obj.api_slug == slug }
|
72
|
+
find_by(slug: slug, **opts)
|
53
73
|
end
|
54
74
|
|
55
75
|
# Get standard objects
|
@@ -251,19 +251,6 @@ module Attio
|
|
251
251
|
super(values: values, **opts)
|
252
252
|
end
|
253
253
|
|
254
|
-
# Find people by email
|
255
|
-
# @param email [String] Email address to search for
|
256
|
-
def find_by_email(email, **opts)
|
257
|
-
list(**opts.merge(
|
258
|
-
filter: {
|
259
|
-
email_addresses: {
|
260
|
-
email_address: {
|
261
|
-
"$eq": email
|
262
|
-
}
|
263
|
-
}
|
264
|
-
}
|
265
|
-
)).first
|
266
|
-
end
|
267
254
|
|
268
255
|
# Search people by query
|
269
256
|
# @param query [String] Query to search for
|
@@ -280,11 +267,28 @@ module Attio
|
|
280
267
|
))
|
281
268
|
end
|
282
269
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
270
|
+
private
|
271
|
+
|
272
|
+
# Build filter for email field
|
273
|
+
def filter_by_email(value)
|
274
|
+
{
|
275
|
+
email_addresses: {
|
276
|
+
email_address: {
|
277
|
+
"$eq": value
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
end
|
282
|
+
|
283
|
+
# Build filter for name field (searches across first, last, and full name)
|
284
|
+
def filter_by_name(value)
|
285
|
+
{
|
286
|
+
"$or": [
|
287
|
+
{name: {first_name: {"$contains": value}}},
|
288
|
+
{name: {last_name: {"$contains": value}}},
|
289
|
+
{name: {full_name: {"$contains": value}}}
|
290
|
+
]
|
291
|
+
}
|
288
292
|
end
|
289
293
|
end
|
290
294
|
end
|
@@ -61,12 +61,55 @@ module Attio
|
|
61
61
|
end
|
62
62
|
|
63
63
|
# Find by a specific attribute value
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
64
|
+
# Supports Rails-style hash syntax: find_by(name: "Test")
|
65
|
+
def find_by(**conditions)
|
66
|
+
raise ArgumentError, "find_by requires at least one condition" if conditions.empty?
|
67
|
+
|
68
|
+
# Extract any opts that aren't conditions (like api_key)
|
69
|
+
opts = {}
|
70
|
+
known_opts = [:api_key, :timeout, :idempotency_key]
|
71
|
+
known_opts.each do |opt|
|
72
|
+
opts[opt] = conditions.delete(opt) if conditions.key?(opt)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Build filter from conditions
|
76
|
+
filters = []
|
77
|
+
search_query = nil
|
78
|
+
|
79
|
+
conditions.each do |field, value|
|
80
|
+
# Check if there's a special filter method for this field
|
81
|
+
filter_method = "filter_by_#{field}"
|
82
|
+
if respond_to?(filter_method, true) # true = include private methods
|
83
|
+
result = send(filter_method, value)
|
84
|
+
# Check if this should be a search instead of a filter
|
85
|
+
if result == :use_search
|
86
|
+
search_query = value
|
87
|
+
else
|
88
|
+
filters << result
|
89
|
+
end
|
90
|
+
else
|
91
|
+
# Use the field as-is
|
92
|
+
filters << {field => value}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# If we have a search query, use search instead of filter
|
97
|
+
if search_query
|
98
|
+
search(search_query, **opts).first
|
99
|
+
else
|
100
|
+
# Combine multiple filters with $and if needed
|
101
|
+
final_filter = if filters.length == 1
|
102
|
+
filters.first
|
103
|
+
elsif filters.length > 1
|
104
|
+
{"$and": filters}
|
105
|
+
else
|
106
|
+
{}
|
107
|
+
end
|
108
|
+
|
109
|
+
list(**opts.merge(params: {
|
110
|
+
filter: final_filter
|
111
|
+
})).first
|
112
|
+
end
|
70
113
|
end
|
71
114
|
end
|
72
115
|
|
@@ -103,10 +103,23 @@ module Attio
|
|
103
103
|
end
|
104
104
|
alias_method :current, :me
|
105
105
|
|
106
|
-
# Find member by
|
107
|
-
def
|
108
|
-
|
109
|
-
|
106
|
+
# Find member by attribute using Rails-style syntax
|
107
|
+
def find_by(**conditions)
|
108
|
+
# Extract any opts that aren't conditions
|
109
|
+
opts = {}
|
110
|
+
known_opts = [:api_key, :timeout, :idempotency_key]
|
111
|
+
known_opts.each do |opt|
|
112
|
+
opts[opt] = conditions.delete(opt) if conditions.key?(opt)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Currently only supports email
|
116
|
+
if conditions.key?(:email)
|
117
|
+
email = conditions[:email]
|
118
|
+
list(**opts).find { |member| member.email_address == email } ||
|
119
|
+
raise(NotFoundError, "Workspace member with email '#{email}' not found")
|
120
|
+
else
|
121
|
+
raise ArgumentError, "find_by only supports email attribute for workspace members"
|
122
|
+
end
|
110
123
|
end
|
111
124
|
|
112
125
|
# List active members only
|
data/lib/attio/version.rb
CHANGED
data/lib/attio.rb
CHANGED
@@ -11,6 +11,7 @@ require_relative "attio/resources/object"
|
|
11
11
|
require_relative "attio/resources/typed_record"
|
12
12
|
require_relative "attio/resources/person"
|
13
13
|
require_relative "attio/resources/company"
|
14
|
+
require_relative "attio/resources/deal"
|
14
15
|
require_relative "attio/resources/attribute"
|
15
16
|
require_relative "attio/resources/list"
|
16
17
|
require_relative "attio/resources/webhook"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attio-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Beene
|
@@ -335,9 +335,10 @@ files:
|
|
335
335
|
- LICENSE
|
336
336
|
- README.md
|
337
337
|
- Rakefile
|
338
|
-
- attio-ruby.gemspec
|
339
338
|
- docs/CODECOV_SETUP.md
|
339
|
+
- examples/app_specific_typed_record.md
|
340
340
|
- examples/basic_usage.rb
|
341
|
+
- examples/deals.rb
|
341
342
|
- examples/oauth_flow.rb
|
342
343
|
- examples/oauth_flow_README.md
|
343
344
|
- examples/typed_records_example.rb
|
@@ -354,6 +355,7 @@ files:
|
|
354
355
|
- lib/attio/resources/attribute.rb
|
355
356
|
- lib/attio/resources/comment.rb
|
356
357
|
- lib/attio/resources/company.rb
|
358
|
+
- lib/attio/resources/deal.rb
|
357
359
|
- lib/attio/resources/entry.rb
|
358
360
|
- lib/attio/resources/list.rb
|
359
361
|
- lib/attio/resources/meta.rb
|
data/attio-ruby.gemspec
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/attio/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "attio-ruby"
|
7
|
-
spec.version = Attio::VERSION
|
8
|
-
spec.authors = ["Robert Beene"]
|
9
|
-
spec.email = ["robert@ismly.com"]
|
10
|
-
|
11
|
-
spec.summary = "Ruby client library for the Attio API"
|
12
|
-
spec.description = "A comprehensive Ruby client library for the Attio CRM API with OAuth support, type safety, and extensive test coverage"
|
13
|
-
spec.homepage = "https://github.com/rbeene/attio_ruby"
|
14
|
-
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">= 3.4.0"
|
16
|
-
|
17
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
-
spec.metadata["source_code_uri"] = "https://github.com/rbeene/attio_ruby"
|
20
|
-
spec.metadata["changelog_uri"] = "https://github.com/rbeene/attio_ruby/blob/main/CHANGELOG.md"
|
21
|
-
spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/attio-ruby"
|
22
|
-
spec.metadata["bug_tracker_uri"] = "https://github.com/rbeene/attio_ruby/issues"
|
23
|
-
|
24
|
-
# Specify which files should be added to the gem when it is released.
|
25
|
-
spec.files = Dir.chdir(__dir__) do
|
26
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
-
(File.expand_path(f) == __FILE__) ||
|
28
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
|
29
|
-
end
|
30
|
-
end
|
31
|
-
spec.bindir = "exe"
|
32
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
33
|
-
spec.require_paths = ["lib"]
|
34
|
-
|
35
|
-
# Runtime dependencies
|
36
|
-
spec.add_dependency "faraday", "~> 2.0"
|
37
|
-
spec.add_dependency "faraday-retry", "~> 2.0"
|
38
|
-
spec.add_dependency "ostruct", "~> 0.6"
|
39
|
-
|
40
|
-
# Development dependencies
|
41
|
-
spec.add_development_dependency "bundler", "~> 2.0"
|
42
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
43
|
-
spec.add_development_dependency "rspec", "~> 3.12"
|
44
|
-
spec.add_development_dependency "webmock", "~> 3.18"
|
45
|
-
spec.add_development_dependency "simplecov", "~> 0.22"
|
46
|
-
spec.add_development_dependency "simplecov-cobertura", "~> 2.1"
|
47
|
-
spec.add_development_dependency "yard", "~> 0.9"
|
48
|
-
spec.add_development_dependency "redcarpet", "~> 3.6"
|
49
|
-
spec.add_development_dependency "rubocop", "~> 1.50"
|
50
|
-
spec.add_development_dependency "rubocop-rspec", "~> 2.20"
|
51
|
-
spec.add_development_dependency "rubocop-performance", "~> 1.17"
|
52
|
-
spec.add_development_dependency "standard", "~> 1.28"
|
53
|
-
spec.add_development_dependency "benchmark-ips", "~> 2.12"
|
54
|
-
spec.add_development_dependency "pry", "~> 0.14"
|
55
|
-
spec.add_development_dependency "pry-byebug", "~> 3.10"
|
56
|
-
spec.add_development_dependency "dotenv", "~> 2.8"
|
57
|
-
spec.add_development_dependency "timecop", "~> 0.9"
|
58
|
-
spec.add_development_dependency "bundle-audit", "~> 0.1"
|
59
|
-
spec.add_development_dependency "brakeman", "~> 6.0"
|
60
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
61
|
-
end
|