rubyhexagon 1.6.4 → 2.0.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.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/lib/rubyhexagon.rb +34 -18
  3. data/lib/rubyhexagon/api.rb +96 -0
  4. data/lib/rubyhexagon/{login.rb → api/artist.rb} +9 -19
  5. data/lib/rubyhexagon/api/note.rb +50 -0
  6. data/lib/rubyhexagon/api/pool.rb +51 -0
  7. data/lib/rubyhexagon/api/post.rb +92 -0
  8. data/lib/rubyhexagon/api/post/flag.rb +36 -0
  9. data/lib/rubyhexagon/api/post/tag_item.rb +36 -0
  10. data/lib/rubyhexagon/api/tag.rb +65 -0
  11. data/lib/rubyhexagon/api/tag/alias.rb +41 -0
  12. data/lib/rubyhexagon/api/tag/implication.rb +41 -0
  13. data/lib/rubyhexagon/api/user.rb +52 -0
  14. data/lib/rubyhexagon/artist.rb +44 -32
  15. data/lib/rubyhexagon/error.rb +4 -5
  16. data/lib/rubyhexagon/note.rb +112 -0
  17. data/lib/rubyhexagon/pool.rb +22 -50
  18. data/lib/rubyhexagon/post.rb +142 -161
  19. data/lib/rubyhexagon/post/flag.rb +78 -0
  20. data/lib/rubyhexagon/post/image.rb +114 -0
  21. data/lib/rubyhexagon/post/tag_item.rb +74 -0
  22. data/lib/rubyhexagon/search/posts.rb +3 -3
  23. data/lib/rubyhexagon/tag.rb +20 -47
  24. data/lib/rubyhexagon/tag/alias.rb +87 -0
  25. data/lib/rubyhexagon/tag/implication.rb +91 -0
  26. data/lib/rubyhexagon/tag/type.rb +79 -0
  27. data/lib/rubyhexagon/user.rb +24 -30
  28. data/lib/rubyhexagon/user/level.rb +92 -0
  29. metadata +22 -13
  30. data/lib/rubyhexagon/helper/api.rb +0 -99
  31. data/lib/rubyhexagon/image.rb +0 -122
  32. data/lib/rubyhexagon/level.rb +0 -58
  33. data/lib/rubyhexagon/search/flag_history.rb +0 -62
  34. data/lib/rubyhexagon/search/pools.rb +0 -62
  35. data/lib/rubyhexagon/search/tag_history.rb +0 -61
  36. data/lib/rubyhexagon/search/tags.rb +0 -139
  37. data/lib/rubyhexagon/tag_change.rb +0 -69
  38. data/lib/rubyhexagon/type.rb +0 -72
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
3
+ # Copyright 2014-2018, 2020 Maxine Michalski <maxine@furfind.net>
4
4
  #
5
5
  # This file is part of rubyhexagon.
6
6
  #
@@ -38,8 +38,11 @@ module Rubyhexagon
38
38
  # @return [String] returns a description of the current pool
39
39
  attr_reader :description
40
40
 
41
- # @return [User] returns the user, who has created this pool
42
- attr_reader :user
41
+ # @return [Integer] returns number of posts inside pool
42
+ attr_reader :post_count
43
+
44
+ # @return [E621::User] returns the user, who has created this pool
45
+ attr_reader :creator
43
46
 
44
47
  # @return [Array<Post>] returns an array of posts, that belong to this pool
45
48
  attr_reader :posts
@@ -48,52 +51,26 @@ module Rubyhexagon
48
51
  #
49
52
  # Initializer for Pool objects
50
53
  #
51
- # @param id [Integer] ID of this pool
54
+ # @param pool [Hash] Pool information
52
55
  #
53
56
  # @return the object
54
- def initialize(id)
55
- unless id.is_a?(Integer) && id.positive?
56
- raise InvalidIDError, "ID out of range: #{id}"
57
- end
58
- @id = id
59
- end
60
-
61
- # @author Maxine Michalski
62
- #
63
- # @param pool [Hash] pool data, if present
64
- #
65
- # Fetch data from e621 to current post id
66
- def show!(pool = nil)
67
- @posts = []
68
- if pool.nil?
69
- page = 1
70
- loop do
71
- pool = API.new.fetch('pool', 'show', id: @id, page: page)
72
- page += 1
73
- @posts += pool[:posts].map do |post|
74
- E621::Post.new(post[:id]).show(post)
57
+ def initialize(pool)
58
+ raise ArgumentError, "#{pool.class} is not a Hash" unless pool.is_a?(Hash)
59
+ raise ArgumentError, 'Hash must include :id' if pool[:id].nil?
60
+ pool.each do |k, v|
61
+ if %i[id name post_count is_active is_locked description].include?(k)
62
+ if k == :id && !(v.is_a?(Integer) && v.positive?)
63
+ raise InvalidIDError, "ID out of range: #{v}"
75
64
  end
76
- break if pool[:posts] == []
65
+ instance_variable_set("@#{k}".to_sym, v)
66
+ elsif %i[updated_at created_at].include?(k)
67
+ instance_variable_set("@#{k}".to_sym, Time.at(v[:s]))
68
+ elsif k == :user_id
69
+ @creator = E621::User.new(id: v)
70
+ elsif k == :posts
71
+ @posts = v.map { |post| E621::Post.new(post) }
77
72
  end
78
73
  end
79
- @name = pool[:name]
80
- @created_at = Time.at(pool[:created_at][:s].to_i)
81
- @updated_at = Time.at(pool[:updated_at][:s].to_i)
82
- @description = pool[:description]
83
- @user = User.new(id: pool[:user_id])
84
- @is_locked = pool[:is_locked]
85
- @is_active = pool[:is_active]
86
- end
87
-
88
- # @author Maxine Michalski
89
- #
90
- # @param pool [Hash] pool data, if present
91
- #
92
- # Fetch data from e621 to current pool id
93
- def show(pool = nil)
94
- tmp = Pool.new(@id)
95
- tmp.show!(pool)
96
- tmp
97
74
  end
98
75
 
99
76
  # @author Maxine Michalski
@@ -122,12 +99,7 @@ module Rubyhexagon
122
99
  #
123
100
  # @return [TrueClass,FalseClass]
124
101
  def ==(other)
125
- if @name.nil?
126
- other.is_a?(Pool) && @id == other.id
127
- else
128
- other.is_a?(Pool) && @name == other.name && @id == other.id &&
129
- @updated_at == other.updated_at
130
- end
102
+ other.is_a?(Pool) && @id == other.id && @updated_at == other.updated_at
131
103
  end
132
104
  end
133
105
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
3
+ # Copyright 2014-2018, 2020 Maxine Michalski <maxine@furfind.net>
4
4
  #
5
5
  # This file is part of rubyhexagon.
6
6
  #
@@ -26,7 +26,7 @@ module Rubyhexagon
26
26
  # @return [Integer] id of post information
27
27
  attr_reader :id
28
28
 
29
- # @return [User] user object of who uploaded this post
29
+ # @return [E621::User] user object of who uploaded this post
30
30
  attr_reader :author
31
31
 
32
32
  # @return [Time] creation time of post
@@ -40,15 +40,15 @@ module Rubyhexagon
40
40
  # form
41
41
  attr_reader :sources
42
42
 
43
- # @return [Array<Artist>] array of artists, that work on the image in this
44
- # post
43
+ # @return [Array<E621::Artist>] array of artists, that work on the image
44
+ # in this post
45
45
  attr_reader :artists
46
46
 
47
47
  # @return [String] description that's associated with this post
48
48
  attr_reader :description
49
49
 
50
50
  # @return [Integer] number of people, who favorited this post
51
- attr_reader :favorites
51
+ attr_reader :fav_count
52
52
 
53
53
  # @return [Integer] vote score this post holds
54
54
  attr_reader :score
@@ -57,252 +57,233 @@ module Rubyhexagon
57
57
  # :questionable, :explicit
58
58
  attr_reader :rating
59
59
 
60
- # @return [Post] parent of this post
60
+ # @return [E621:post] parent of this post
61
61
  # @return [NilClass]
62
62
  attr_reader :parent
63
63
 
64
- # @return [Array<Post>] Array of child posts
64
+ # @return [Array<E621::Post>] Array of child posts
65
65
  attr_reader :children
66
66
 
67
67
  # @return [String] MD5 checksum associated with this post
68
68
  attr_reader :md5
69
69
 
70
- # @return [Rubyhexagon::Image] file content of this post
70
+ # @return [E621::Image] file content of this post
71
71
  attr_reader :image
72
72
 
73
- # @return [Rubyhexagon::Sample] sample data in Rubyhexagon::Sample format
73
+ # @return [E621::Sample] sample data in E621::Sample format
74
74
  attr_reader :sample
75
75
 
76
- # @return [Rubyhexagon::Preview] preview data in Rubyhexagon::Preview format
76
+ # @return [E621::Preview] preview data in E621::Preview format
77
77
  attr_reader :preview
78
78
 
79
- # @return [Array<String>] tag names only for convenience
80
- attr_reader :tag_names
79
+ # @return [String|NilClass] reason for deletion, if deleted
80
+ attr_reader :delreason
81
+
82
+ # @return [Array<E621::Tag>] tags of this post
83
+ attr_reader :tags
81
84
 
82
85
  # @author Maxine Michalski
83
86
  #
84
87
  # Initializer for Post.
85
88
  #
86
- # @param id [Integer] post id on e621
89
+ # @param post [Hash] post data
87
90
  #
88
91
  # @return the object
89
- def initialize(id)
90
- id = id.to_i
92
+ def initialize(post)
93
+ raise ArgumentError, "#{post.class} is not a Hash" unless post.is_a?(Hash)
94
+ post[:id] = post[:id].to_i
95
+ id = post[:id]
91
96
  raise InvalidIDError, "ID out of range: #{id}" unless id.positive?
92
- @id = id
97
+ post.each do |k, v|
98
+ if %i[rating created_at status sources parent_id children
99
+ description artist tags].include?(k)
100
+ __send__("setup_#{k}".to_sym, v)
101
+ elsif %i[id description score fav_count md5 has_comments has_children
102
+ delreason has_notes].include?(k)
103
+ instance_variable_set("@#{k}".to_sym, v)
104
+ end
105
+ end
106
+ setup_author(post)
107
+ setup_files(post)
93
108
  end
94
109
 
95
110
  # @author Maxine Michalski
96
111
  #
97
- # @param post [Hash] post data, if present
112
+ # Test status with an optional parameter. This is more meant to be used by
113
+ # alias methods
98
114
  #
99
- # Fetch data from e621 to current post id
100
- def show!(post = nil)
101
- post = API.new.fetch('post', 'show', id: @id) if post.nil?
102
- @created_at = Time.at(post[:created_at][:s].to_i)
103
- @status = post[:status].to_sym
104
- setup_author(post)
105
- setup_relatives(post)
106
- setup_file_data(post)
107
- setup_additional_data(post)
115
+ # @param test_var [Symbol] variable under test
116
+ #
117
+ # @return [TrueClass]
118
+ # @return [FalseClass]
119
+ def test(test_var = nil)
120
+ test_var ||= __callee__.to_s.sub(/\?/, '').to_sym
121
+ test_ar = %i[safe questionable explicit]
122
+ test_ar.include?(test_var) ? @rating == test_var : @status == test_var
108
123
  end
124
+ alias active? test
125
+ alias flagged? test
126
+ alias pending? test
127
+ alias deleted? test
128
+ alias safe? test
129
+ alias questionable? test
130
+ alias explicit? test
109
131
 
110
132
  # @author Maxine Michalski
111
133
  #
112
- # @param post [Hash] post data, if present
134
+ # Show if post has comments or not
113
135
  #
114
- # Fetch data from e621 to current post id
115
- def show(post = nil)
116
- tmp = Post.new(@id)
117
- tmp.show!(post)
118
- tmp
136
+ # @return [TrueClass|FalseClass]
137
+ def comments?
138
+ return false if @has_comments.nil?
139
+ @has_comments
119
140
  end
120
141
 
121
142
  # @author Maxine Michalski
122
143
  #
123
- # Send a vote to e621
144
+ # Show if post has a parent
124
145
  #
125
- # @param score [Integer] direction of vote, can be positive or negative
126
- def vote!(score)
127
- unless score.is_a?(Integer)
128
- raise ArgumentError, 'Voting requires a positive/negative number'
129
- end
130
- raise ArgumentError, "Vote can't be zero" if score.zero?
131
- score = score.positive? ? 1 : -1
132
- res = API.new.fetch('post', 'vote', { id: @id, score: score }, 'POST')
133
- if res[:reason] == 'access denied' && !res[:success]
134
- raise Rubyhexagon::AccessDenied, res[:message]
135
- end
136
- nil
146
+ # @return [TrueClass|FalseClass]
147
+ def parent?
148
+ !@parent.nil?
137
149
  end
138
150
 
139
151
  # @author Maxine Michalski
140
152
  #
141
- # Upvotes this post
142
- def voteup!
143
- vote!(1)
144
- end
145
-
146
- # @author Maxine Michalski
153
+ # Show if post has children
147
154
  #
148
- # Downvotes this post
149
- def votedown!
150
- vote!(-1)
155
+ # @return [TrueClass|FalseClass]
156
+ def children?
157
+ return false if @has_children.nil?
158
+ @has_children
151
159
  end
152
160
 
153
161
  # @author Maxine Michalski
154
162
  #
155
- # Favorite this post
156
- def favorite!
157
- res = API.new.fetch('favorite', 'create', { id: @id }, 'POST')
158
- if res[:reason] == 'access denied' && !res[:success]
159
- raise Rubyhexagon::AccessDenied, res[:message]
160
- elsif !res[:success] &&
161
- res[:reason] == 'You\'ve already favorited this post'
162
- raise Rubyhexagon::AlreadyFavorited
163
- end
164
- nil
163
+ # Comparison method for posts
164
+ #
165
+ # @return [TrueClass, FalseClass]
166
+ def ==(other)
167
+ other.is_a?(Post) && @id == other.id
165
168
  end
166
169
 
167
170
  # @author Maxine Michalski
168
171
  #
169
- # Unfavorite this post
170
- def unfavorite!
171
- res = API.new.fetch('favorite', 'destroy', { id: @id }, 'POST')
172
- if res[:reason] == 'access denied' && !res[:success]
173
- raise Rubyhexagon::AccessDenied, res[:message]
174
- end
175
- nil
172
+ # Has post notes?
173
+ #
174
+ # @return [TrueClass, FalseClass]
175
+ def notes?
176
+ @has_notes
176
177
  end
177
178
 
179
+ private
180
+
178
181
  # @author Maxine Michalski
179
182
  #
180
- # Retrieve an Array of this post's tags
183
+ # Set author information
181
184
  #
182
- # @return [Array<Tag>] array of tags
183
- def tags
184
- API.new.fetch('post', 'tags', id: @id).map do |tag|
185
- Tag.new(tag[:id]).show(tag)
186
- end
185
+ # @param post [Hash] post data
186
+ def setup_author(post)
187
+ return if post[:creator_id].nil?
188
+ @author = E621::User.new(id: post[:creator_id], name: post[:author])
187
189
  end
188
190
 
189
191
  # @author Maxine Michalski
190
192
  #
191
- # Test status with an optional parameter. This is more meant to be used by
192
- # alias methods
193
+ # Set rating for post
193
194
  #
194
- # @param test_var [Symbol] variable under test
195
- #
196
- # @return [TrueClass]
197
- # @return [FalseClass]
198
- def test(test_var = nil)
199
- test_var ||= __callee__.to_s.sub(/\?/, '').to_sym
200
- test_ar = %i[safe questionable explicit]
201
- test_ar.include?(test_var) ? @rating == test_var : @status == test_var
195
+ # @param rating [String] rating of this post
196
+ def setup_rating(rating)
197
+ @rating = case rating
198
+ when 's' then :safe
199
+ when 'q' then :questionable
200
+ when 'e' then :explicit
201
+ end
202
202
  end
203
- alias active? test
204
- alias flagged? test
205
- alias pending? test
206
- alias deleted? test
207
- alias safe? test
208
- alias questionable? test
209
- alias explicit? test
210
203
 
211
204
  # @author Maxine Michalski
212
205
  #
213
- # Comparison method for posts
206
+ # Set creation time
214
207
  #
215
- # @return [TrueClass, FalseClass]
216
- def ==(other)
217
- other.is_a?(Post) && @id == other.id
208
+ # @param time [Time] Hash that includes creation time in UNIX Epoch on an
209
+ # `:s` key.
210
+ def setup_created_at(time)
211
+ @created_at = Time.at(time[:s]) unless time.nil?
218
212
  end
219
213
 
220
- private
221
-
222
214
  # @author Maxine Michalski
223
215
  #
224
- # Helper function, to setup post file data
225
- def setup_file_data(post)
226
- @tag_names = post[:tags].split(/\s+/)
227
- @md5 = post[:md5]
228
- @image = Image.new(url: post[:file_url], ext: post[:file_ext],
229
- width: post[:width], height: post[:height],
230
- size: post[:file_size])
231
- setup_sample(post)
232
- setup_preview(post)
216
+ # Set status of post
217
+ #
218
+ # @param status [String] Status of post
219
+ def setup_status(status)
220
+ @status = status.to_sym unless status.nil?
233
221
  end
234
222
 
235
223
  # @author Maxine Michalski
236
224
  #
237
- # Helper function, to setup post sample data
238
- def setup_sample(post)
239
- @sample = Sample.new(url: post[:sample_url], ext: post[:file_ext],
240
- width: post[:sample_width],
241
- height: post[:sample_height])
225
+ # Set sources of post
226
+ #
227
+ # @param sources [Array<String>] Sources of post
228
+ def setup_sources(sources)
229
+ @sources = sources.nil? ? [] : sources
242
230
  end
243
231
 
244
232
  # @author Maxine Michalski
245
233
  #
246
- # Helper function, to setup post preview data
247
- def setup_preview(post)
248
- @preview = Preview.new(url: post[:preview_url], ext: 'jpg',
249
- width: post[:preview_width],
250
- height: post[:preview_height])
234
+ # Set parent post
235
+ #
236
+ # @param parent [Integer] Parent post ID
237
+ #
238
+ # @notice A parent post is only set with an ID and further information must
239
+ # be fetched manually.
240
+ def setup_parent_id(parent)
241
+ @parent = parent.nil? ? nil : E621::Post.new(id: parent)
251
242
  end
252
243
 
253
244
  # @author Maxine Michalski
254
245
  #
255
- # Helper function to add additional post data
256
- def setup_additional_data(post)
257
- @sources = post[:source].nil? ? [] : post[:sources]
258
- @artists = post[:artist].map { |a| Artist.new(a) }
259
- @description = post[:description]
260
- @favorites = post[:fav_count].to_i
261
- @score = post[:score].to_i
262
- @rating = case post[:rating]
263
- when 's' then :safe
264
- when 'q' then :questionable
265
- when 'e' then :explicit
266
- end
246
+ # Set children posts
247
+ #
248
+ # @param children [Array<Integer>] Children post IDs
249
+ #
250
+ # @notice All children are only set with an ID and further information must
251
+ # be fetched manually.
252
+ def setup_children(children)
253
+ return [] if children.nil?
254
+ @children = children.split(',').map { |c| E621::Post.new(id: c) }
267
255
  end
268
256
 
269
257
  # @author Maxine Michalski
270
258
  #
271
- # Helper function to related posts (children, parent)
272
- def setup_relatives(post)
273
- @children = post[:children].split(',').map { |i| Post.new(i) }
274
- @parent = post[:parent_id].nil? ? nil : Post.new(post[:parent_id])
259
+ # Set post description
260
+ #
261
+ # @param description [String] Post description
262
+ #
263
+ # @notice Empty descriptions are set to nil
264
+ def setup_description(description)
265
+ @description = !description.nil? && description.empty? ? nil : description
275
266
  end
276
267
 
277
- # @author Maxine Michalski
278
- #
279
- # Helper function to set author of a post
280
- def setup_author(post)
281
- @author = User.new(id: post[:creator_id], name: post[:author])
268
+ def setup_artist(artists)
269
+ @artists = artists.map { |a| E621::Artist.new(name: CGI.unescape(a)) }
282
270
  end
283
- end
284
271
 
285
- # This class holds post information, fetched from e621, and gives access in a
286
- # more Ruby like manner.
287
- # This is only for deleted posts.
288
- #
289
- # @author Maxine Michalski
290
- # @since 1.0.0
291
- class DeletedPost < Post
292
- # @return [String] deletion reason, when available
293
- attr_reader :reason
272
+ def setup_tags(tags)
273
+ @tags = tags.split(' ').map { |t| E621::Tag.new(name: t) }
274
+ end
294
275
 
295
- # @author Maxine Michalski
296
- #
297
- # Initializer for DeletedPost.
298
- #
299
- # @param id [Integer] post id on e621
300
- # @param delreason [String] reason of deletion
301
- #
302
- # @return the object
303
- def initialize(id, delreason)
304
- super(id)
305
- @reason = delreason
276
+ def setup_files(post)
277
+ return if post[:file_url].nil?
278
+ @image = Image.new(url: post[:file_url], ext: post[:file_ext],
279
+ width: post[:width], height: post[:height],
280
+ size: post[:file_size])
281
+ @sample = Sample.new(url: post[:sample_url], ext: post[:file_ext],
282
+ width: post[:sample_width],
283
+ height: post[:sample_height], size: 0)
284
+ @preview = Preview.new(url: post[:preview_url], ext: 'jpg',
285
+ width: post[:preview_width],
286
+ height: post[:preview_height], size: 0)
306
287
  end
307
288
  end
308
289
  end