tweetkit 0.1.1 → 0.2.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.
@@ -1,65 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
- require 'pry'
3
4
 
4
5
  module Tweetkit
5
- class Response
6
- attr_accessor :expansions, :meta, :original_response, :tweets
7
-
8
- def initialize(response)
9
- @original_response = response.body
10
- parsed_response = JSON.parse(@original_response)
11
- @tweets = Tweetkit::Response::Tweets.new(parsed_response)
12
- @meta = Tweetkit::Response::Meta.new(@tweets.meta)
13
- @expansions = Tweetkit::Response::Expansions.new(@tweets.expansions)
14
- end
15
-
16
- class Expansions
6
+ module Response
7
+ class Tweets
17
8
  include Enumerable
18
9
 
19
- attr_accessor :expansions
10
+ attr_accessor :annotations, :connection, :context_annotations, :entity_annotations, :expansions, :fields, :meta, :options, :original_response, :response, :tweets, :twitter_request
20
11
 
21
- def initialize(expansions)
22
- @expansions = expansions
12
+ def initialize(response, **options)
13
+ parse! response, **options
23
14
  end
24
15
 
25
- def method_missing(attribute, **args)
26
- data = expansions[attribute.to_s]
27
- data.empty? ? super : data
28
- end
16
+ def parse!(response, **options)
17
+ parse_response response
18
+ extract_and_save_tweets
19
+ return unless @tweets
29
20
 
30
- def respond_to_missing?(method)
31
- expansions.respond_to? method
21
+ extract_and_save_meta
22
+ extract_and_save_expansions
23
+ extract_and_save_options(**options)
24
+ extract_and_save_request
32
25
  end
33
- end
34
-
35
- class Meta
36
- include Enumerable
37
-
38
- attr_accessor :meta
39
26
 
40
- def initialize(meta)
41
- @meta = meta
27
+ def parse_response(response)
28
+ @original_response = response.body
29
+ @response = JSON.parse(@original_response)
42
30
  end
43
31
 
44
- def method_missing(attribute, **args)
45
- data = meta[attribute.to_s]
46
- data.empty? ? super : data
32
+ def extract_and_save_tweets
33
+ if (data = @response['data'])
34
+ if data.is_a?(Array)
35
+ @tweets = @response['data'].collect { |tweet| Tweet.new(tweet) }
36
+ else
37
+ @tweets = [Tweet.new(@response['data'])]
38
+ end
39
+ else
40
+ @tweets = nil
41
+ end
47
42
  end
48
43
 
49
- def respond_to_missing?(method)
50
- meta.respond_to? method
44
+ def extract_and_save_meta
45
+ @meta = Meta.new(@response['meta'])
51
46
  end
52
- end
53
47
 
54
- class Tweets
55
- include Enumerable
48
+ def extract_and_save_expansions
49
+ @expansions = Expansions.new(@response['includes'])
50
+ end
56
51
 
57
- attr_accessor :tweets, :meta, :expansions
52
+ def extract_and_save_options(**options)
53
+ @options = options
54
+ end
58
55
 
59
- def initialize(response)
60
- @tweets = response['data'].collect { |tweet| Tweetkit::Response::Tweet.new(tweet) }
61
- @meta = response['meta']
62
- @expansions = response['includes']
56
+ def extract_and_save_request
57
+ @connection = @options[:connection]
58
+ @twitter_request = @options[:twitter_request]
63
59
  end
64
60
 
65
61
  def each(*args, &block)
@@ -70,36 +66,563 @@ module Tweetkit
70
66
  tweets.last
71
67
  end
72
68
 
73
- def to_s
74
- @tweets.join(' ')
69
+ def tweet
70
+ tweets.first
75
71
  end
76
72
 
77
- def method_missing(attribute, **args)
78
- result = tweets.public_send(attribute, **args)
79
- super unless result
80
- rescue StandardError
81
- super
73
+ def next_page
74
+ connection.params.merge!({ next_token: meta.next_token })
75
+ response = connection.get(twitter_request[:previous_url])
76
+ parse! response,
77
+ connection: connection,
78
+ twitter_request: {
79
+ previous_url: twitter_request[:previous_url],
80
+ previous_query: twitter_request[:previous_query]
81
+ }
82
+ self
82
83
  end
83
84
 
84
- def respond_to_missing?(method)
85
- tweets.respond_to? method
85
+ def prev_page
86
+ connection.params.merge!({ previous: meta.previous_token })
87
+ response = connection.get(twitter_request[:previous_url])
88
+ parse! response,
89
+ connection: connection,
90
+ twitter_request: {
91
+ previous_url: twitter_request[:previous_url],
92
+ previous_query: twitter_request[:previous_query]
93
+ }
94
+ self
86
95
  end
87
- end
88
96
 
89
- class Tweet
90
- attr_accessor :tweet
97
+ class Tweet
98
+ attr_accessor :annotations, :attachments, :data
99
+
100
+ def initialize(tweet)
101
+ @data = tweet
102
+ @annotations = Annotations.new(data['context_annotations'], data['entities'])
103
+ @attachments = Attachments.new(data['attachments'])
104
+ end
105
+
106
+ def id
107
+ data['id']
108
+ end
109
+
110
+ def text
111
+ data['text']
112
+ end
113
+
114
+ def author_id
115
+ data['author_id']
116
+ end
117
+
118
+ def conversation_id
119
+ data['conversation_id']
120
+ end
121
+
122
+ def created_at
123
+ data['created_at']
124
+ end
125
+
126
+ def reply_to
127
+ in_reply_to_user_id
128
+ end
129
+
130
+ def in_reply_to_user_id
131
+ data['in_reply_to_user_id']
132
+ end
133
+
134
+ def lang
135
+ data['lang']
136
+ end
137
+
138
+ def nsfw?
139
+ possibly_sensitive
140
+ end
141
+
142
+ def sensitive?
143
+ possibly_sensitive
144
+ end
145
+
146
+ def possibly_sensitive
147
+ data['possibly_sensitive']
148
+ end
149
+
150
+ def permission
151
+ reply_settings
152
+ end
153
+
154
+ def reply_settings
155
+ data['reply_settings']
156
+ end
157
+
158
+ def device
159
+ source
160
+ end
161
+
162
+ def source
163
+ data['source']
164
+ end
165
+
166
+ def withheld?
167
+ withheld && !withheld.empty?
168
+ end
169
+
170
+ def withheld
171
+ data['withheld']
172
+ end
173
+
174
+ def context_annotations
175
+ @annotations.context_annotations || nil
176
+ end
177
+
178
+ def entity_annotations
179
+ entities
180
+ end
181
+
182
+ def entities
183
+ @annotations.entity_annotations || nil
184
+ end
185
+
186
+ class Attachments
187
+ attr_accessor :media_keys, :poll_ids
188
+
189
+ def initialize(attachments)
190
+ return unless attachments
191
+
192
+ @media_keys = attachments['media_keys']
193
+ @poll_ids = attachments['poll_ids']
194
+ end
195
+ end
196
+
197
+ class Annotations
198
+ attr_accessor :context_annotations, :entity_annotations
199
+
200
+ def initialize(context_annotations, entity_annotations)
201
+ return unless context_annotations || entity_annotations
202
+
203
+ @context_annotations = Context.new(context_annotations)
204
+ @entity_annotations = Entity.new(entity_annotations)
205
+ end
206
+
207
+ class Context
208
+ include Enumerable
209
+
210
+ attr_accessor :annotations
211
+
212
+ def initialize(annotations)
213
+ return unless annotations
214
+
215
+ @annotations = annotations.collect { |annotation| Annotation.new(annotation) }
216
+ end
217
+
218
+ def each(*args, &block)
219
+ annotations.each(*args, &block)
220
+ end
221
+
222
+ class Annotation
223
+ attr_accessor :domain, :entity
224
+
225
+ def initialize(annotation)
226
+ @domain = annotation['domain']
227
+ @entity = annotation['entity']
228
+ end
229
+ end
230
+ end
231
+
232
+ class Entity
233
+ include Enumerable
234
+
235
+ attr_accessor :annotations, :cashtags, :hashtags, :mentions, :urls
236
+
237
+ def initialize(entity_annotations)
238
+ return unless entity_annotations
239
+
240
+ @annotations = Annotations.new(entity_annotations['annotations'])
241
+ @cashtags = Cashtags.new(entity_annotations['cashtags'])
242
+ @hashtags = Hashtags.new(entity_annotations['hashtags'])
243
+ @mentions = Mentions.new(entity_annotations['mentions'])
244
+ @urls = Urls.new(entity_annotations['urls'])
245
+ end
246
+
247
+ def each(*args, &block)
248
+ annotations.each(*args, &block)
249
+ end
250
+
251
+ class Annotations
252
+ attr_accessor :annotations
253
+
254
+ def initialize(annotations)
255
+ return unless annotations
256
+
257
+ @annotations = annotations.collect { |annotation| Annotation.new(annotation) }
258
+ end
259
+
260
+ class Annotation
261
+ attr_accessor :end, :probability, :start, :text, :type
262
+
263
+ def initialize(annotation)
264
+ @end = annotation['end']
265
+ @probability = annotation['probability']
266
+ @start = annotation['start']
267
+ @text = annotation['normalized_text']
268
+ @type = annotation['type']
269
+ end
270
+ end
271
+ end
272
+
273
+ class Cashtags
274
+ attr_accessor :cashtags
275
+
276
+ def initialize(cashtags)
277
+ return unless cashtags
278
+
279
+ @cashtags = cashtags.collect { |cashtag| Cashtag.new(cashtag) }
280
+ end
281
+
282
+ class Cashtag
283
+ attr_accessor :end, :start, :tag
284
+
285
+ def initialize(cashtag)
286
+ @end = cashtag['end']
287
+ @start = cashtag['start']
288
+ @tag = cashtag['tag']
289
+ end
290
+ end
291
+ end
292
+
293
+ class Hashtags
294
+ attr_accessor :hashtags
295
+
296
+ def initialize(hashtags)
297
+ return unless hashtags
298
+
299
+ @hashtags = hashtags.collect { |hashtag| Hashtag.new(hashtag) }
300
+ end
301
+
302
+ class Hashtag
303
+ attr_accessor :end, :start, :tag
304
+
305
+ def initialize(hashtag)
306
+ @end = hashtag['end']
307
+ @start = hashtag['start']
308
+ @tag = hashtag['tag']
309
+ end
310
+ end
311
+ end
312
+
313
+ class Mentions
314
+ attr_accessor :mentions
315
+
316
+ def initialize(mentions)
317
+ return unless mentions
91
318
 
92
- def initialize(tweet)
93
- @tweet = tweet
319
+ @mentions = mentions.collect { |mention| Mention.new(mention) }
320
+ end
321
+
322
+ class Mention
323
+ attr_accessor :end, :id, :start, :username
324
+
325
+ def initialize(mention)
326
+ @end = mention['end']
327
+ @id = mention['id']
328
+ @start = mention['start']
329
+ @username = mention['username']
330
+ end
331
+ end
332
+ end
333
+
334
+ class Urls
335
+ attr_accessor :urls
336
+
337
+ def initialize(urls)
338
+ return unless urls
339
+
340
+ @urls = urls.collect { |url| Url.new(url) }
341
+ end
342
+
343
+ class Url
344
+ attr_accessor :description, :display_url, :end, :expanded_url, :start, :status, :title, :url, :unwound_url
345
+
346
+ def initialize(url)
347
+ @description = url['description']
348
+ @display_url = url['display_url']
349
+ @end = url['end']
350
+ @expanded_url = url['expanded_url']
351
+ @start = url['start']
352
+ @status = url['status']
353
+ @title = url['title']
354
+ @url = url['url']
355
+ @unwound_url = url['unwound_url']
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ class Geo
363
+ attr_accessor :coordinates, :place_id
364
+
365
+ def initialize(geo)
366
+ return unless geo
367
+
368
+ @coordinates = Coordinates.new(geo['coordinates'])
369
+ @place_id = geo['place_id']
370
+ end
371
+
372
+ class Coordinates
373
+ attr_accessor :coordinates, :type
374
+
375
+ def initialize(coordinates)
376
+ @coordinates = coordinates['coordinates']
377
+ @type = coordinates['point']
378
+ end
379
+
380
+ def x
381
+ coordinates[0]
382
+ end
383
+
384
+ def y
385
+ coordinates[0]
386
+ end
387
+ end
388
+ end
389
+
390
+ class Metrics
391
+ attr_accessor :public_metrics
392
+
393
+ def initialize(**metrics)
394
+ return unless metrics
395
+
396
+ @public_metrics = Public.new(metrics[:public_metrics])
397
+ end
398
+
399
+ class Public
400
+ attr_accessor :like_count, :quote_count, :reply_count, :retweet_count
401
+
402
+ def initialize(public_metric)
403
+ @like_count = public_metric['like_count']
404
+ @quote_count = public_metric['quote_count']
405
+ @reply_count = public_metric['reply_count']
406
+ @retweet_count = public_metric['retweet_count']
407
+ end
408
+
409
+ def likes
410
+ @like_count
411
+ end
412
+
413
+ def quotes
414
+ @quote_count
415
+ end
416
+
417
+ def replies
418
+ @reply_count
419
+ end
420
+
421
+ def retweets
422
+ @retweet_count
423
+ end
424
+ end
425
+ end
94
426
  end
95
427
 
96
- def method_missing(attribute)
97
- data = tweet[attribute.to_s]
98
- data.empty? ? super : data
428
+ class Expansions
429
+ attr_accessor :media, :places, :polls, :tweets, :users
430
+
431
+ def initialize(expansions)
432
+ return unless expansions
433
+
434
+ @media = Media.new(expansions['media'])
435
+ @places = expansions['places']
436
+ @polls = expansions['polls']
437
+ @tweets = Tweets.new(expansions['tweets'])
438
+ @users = Users.new(expansions['users'])
439
+ end
440
+
441
+ class Media
442
+ attr_accessor :media
443
+
444
+ def initialize(media)
445
+ return unless media
446
+
447
+ @media = media.collect { |media_object| MediaObject.new(media_object) }
448
+ end
449
+
450
+ class MediaObject
451
+ attr_accessor :media_key, :type
452
+
453
+ def initialize(media_object)
454
+ @media_key = media_object['media_key']
455
+ @type = media_object['type']
456
+ end
457
+ end
458
+ end
459
+
460
+ class Places
461
+ attr_accessor :places
462
+
463
+ def initialize(places)
464
+ return unless places
465
+
466
+ @places = places.collect { |place| Place.new(place) }
467
+ end
468
+
469
+ class Place
470
+ attr_accessor :full_name, :id
471
+
472
+ def initialize(place)
473
+ @full_name = place['full_name']
474
+ @id = place['id']
475
+ end
476
+ end
477
+ end
478
+
479
+ class Polls
480
+ attr_accessor :polls
481
+
482
+ def initialize(polls)
483
+ return unless polls
484
+
485
+ @polls = polls.collect { |poll| Poll.new(poll) }
486
+ end
487
+
488
+ class Poll
489
+ attr_accessor :id, :options
490
+
491
+ def initialize(poll)
492
+ @id = poll['id']
493
+ @options = Options.new(poll['options'])
494
+ end
495
+
496
+ class Options
497
+ attr_accessor :options
498
+
499
+ def initialize(options)
500
+ @options = options.collect { |option| Option.new(option) }
501
+ end
502
+
503
+ class Option
504
+ attr_accessor :label, :position, :votes
505
+
506
+ def initialize(option)
507
+ @label = option['label']
508
+ @position = option['position']
509
+ @votes = option['votes']
510
+ end
511
+ end
512
+ end
513
+ end
514
+ end
515
+
516
+ class Tweets
517
+ attr_accessor :tweets
518
+
519
+ def initialize(tweets)
520
+ return unless tweets
521
+
522
+ @tweets = tweets.collect { |tweet| Tweet.new(tweet) }
523
+ end
524
+ end
525
+
526
+ class Users
527
+ attr_accessor :users
528
+
529
+ def initialize(users)
530
+ return unless users
531
+
532
+ @users = users.collect { |user| User.new(user) }
533
+ end
534
+
535
+ class User
536
+ attr_accessor :id, :name, :username
537
+
538
+ def initialize(user)
539
+ @id = user['id']
540
+ @name = user['name']
541
+ @username = user['username']
542
+ end
543
+ end
544
+ end
99
545
  end
100
546
 
101
- def respond_to_missing?(method)
102
- tweet.respond_to? method
547
+ class Fields
548
+ attr_accessor :fields, :media_fields, :place_fields, :poll_fields, :tweet_fields, :user_fields
549
+
550
+ def initialize(fields)
551
+ @fields = fields
552
+ build_and_normalize_fields(fields) unless fields.nil?
553
+ end
554
+
555
+ def build_and_normalize_fields(fields)
556
+ fields.each_key do |field_type|
557
+ normalized_field = build_and_normalize_field(@fields[field_type], field_type)
558
+ instance_variable_set(:"@#{field_type}", normalized_field)
559
+ self.class.define_method(field_type) { instance_variable_get("@#{field_type}") }
560
+ end
561
+ end
562
+
563
+ def build_and_normalize_field(field, field_type)
564
+ Field.new(field, field_type)
565
+ end
566
+
567
+ def method_missing(method, **args)
568
+ return nil if VALID_FIELDS.include?(method.to_s)
569
+
570
+ super
571
+ end
572
+
573
+ def respond_to_missing?(method, *args)
574
+ VALID_FIELDS.include?(method.to_s) || super
575
+ end
576
+
577
+ class Field
578
+ include Enumerable
579
+
580
+ attr_accessor :normalized_field, :original_field
581
+
582
+ FIELD_NORMALIZATION_KEY = {
583
+ 'users': 'id'
584
+ }.freeze
585
+
586
+ def initialize(field, field_type)
587
+ @original_field = field
588
+ @normalized_field = {}
589
+ normalization_key = FIELD_NORMALIZATION_KEY[field_type.to_sym]
590
+ field.each do |data|
591
+ key = data[normalization_key]
592
+ @normalized_field[key.to_i] = data
593
+ end
594
+ end
595
+
596
+ def each(*args, &block)
597
+ @normalized_field.each(*args, &block)
598
+ end
599
+
600
+ def each_data(*args, &block)
601
+ @normalized_field.values.each(*args, &block)
602
+ end
603
+
604
+ def find(key)
605
+ @normalized_field[key.to_i]
606
+ end
607
+ end
608
+ end
609
+
610
+ class Meta
611
+ attr_accessor :data
612
+
613
+ def initialize(meta)
614
+ return unless meta
615
+
616
+ @data = meta
617
+ end
618
+
619
+ def next_token
620
+ @data['next_token']
621
+ end
622
+
623
+ def previous_token
624
+ @data['previous_token']
625
+ end
103
626
  end
104
627
  end
105
628
  end
@@ -1,3 +1,3 @@
1
1
  module Tweetkit
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end