rubyhexagon 0.4.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,256 @@
1
+ # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
2
+ #
3
+ # This file is part of rubyhexagon.
4
+ #
5
+ # rubyhexagon is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # rubyhexagon is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module E621
18
+ # This class holds post information, fetched from e621, and gives access in a
19
+ # more Ruby like manner.
20
+ #
21
+ # @author Maxine Michalski
22
+ # @since 1.0.0
23
+ class Post
24
+ # @return [Integer] id of post information
25
+ attr_reader :id
26
+
27
+ # @return [User] user object of who uploaded this post
28
+ attr_reader :author
29
+
30
+ # @return [Time] creation time of post
31
+ attr_reader :created_at
32
+
33
+ # @return [Symbol] status of post, can be :active, :flagged, :pending or
34
+ # :deleted
35
+ attr_reader :status
36
+
37
+ # @return [Array<String>] array of sources. This can contain URI in string
38
+ # form
39
+ attr_reader :sources
40
+
41
+ # @return [Array<Artist>] array of artists, that work on the image in this
42
+ # post
43
+ attr_reader :artists
44
+
45
+ # @return [String] description that's associated with this post
46
+ attr_reader :description
47
+
48
+ # @return [Integer] number of people, who favorited this post
49
+ attr_reader :favorites
50
+
51
+ # @return [Integer] vote score this post holds
52
+ attr_reader :score
53
+
54
+ # @return [Symbol] rating of this post. Can be one of :safe,
55
+ # :questionable, :explicit
56
+ attr_reader :rating
57
+
58
+ # @return [Post] parent of this post
59
+ # @return [NilClass]
60
+ attr_reader :parent
61
+
62
+ # @return [Array<Post>] Array of child posts
63
+ attr_reader :children
64
+
65
+ # @return [String] MD5 checksum associated with this post
66
+ attr_reader :md5
67
+
68
+ # @return [E621::Image] file content of this post
69
+ attr_reader :image
70
+
71
+ # @return [E621::Sample] sample data in E621::Sample format
72
+ attr_reader :sample
73
+
74
+ # @return [E621::Preview] preview data in E621::Preview format
75
+ attr_reader :preview
76
+
77
+ # @author Maxine Michalski
78
+ #
79
+ # Initializer for Post.
80
+ #
81
+ # @param id [Integer] post id on e621
82
+ #
83
+ # @return the object
84
+ def initialize(id)
85
+ id = id.to_i
86
+ unless id.is_a?(Integer) && id > 0
87
+ raise InvalidIDError, "ID out of range: #{id}"
88
+ end
89
+ @id = id
90
+ end
91
+
92
+ # @author Maxine Michalski
93
+ #
94
+ # @param post [Hash] post data, if present
95
+ #
96
+ # Fetch data from e621 to current post id
97
+ def show!(post = nil)
98
+ post = API.new.fetch('post', 'show', id: @id) if post.nil?
99
+ @created_at = Time.at(post[:created_at][:s].to_i)
100
+ @status = post[:status].to_sym
101
+ setup_author(post)
102
+ setup_relatives(post)
103
+ setup_file_data(post)
104
+ setup_additional_data(post)
105
+ end
106
+
107
+ # @author Maxine Michalski
108
+ #
109
+ # @param post [Hash] post data, if present
110
+ #
111
+ # Fetch data from e621 to current post id
112
+ def show(post = nil)
113
+ post = API.new.fetch('post', 'show', id: @id) if post.nil?
114
+ tmp = Post.new(post[:id].to_i)
115
+ tmp.show!(post)
116
+ tmp
117
+ end
118
+
119
+ # @author Maxine Michalski
120
+ #
121
+ # Retrieve an Array of this post's tags
122
+ #
123
+ # @return [Array<Tag>] array of tags
124
+ def tags
125
+ API.new.fetch('post', 'tags', id: @id).map do |tag|
126
+ Tag.new(tag[:id]).show(tag)
127
+ end
128
+ end
129
+
130
+ # @author Maxine Michalski
131
+ #
132
+ # Test status with an optional parameter. This is more meant to be used by
133
+ # alias methods
134
+ #
135
+ # @return [TrueClass]
136
+ # @return [FalseClass]
137
+ def test_status(status = nil)
138
+ status ||= __callee__.to_s.sub(/\?/, '').to_sym
139
+ @status == status
140
+ end
141
+ alias active? test_status
142
+ alias flagged? test_status
143
+ alias pending? test_status
144
+ alias deleted? test_status
145
+
146
+ # @author Maxine Michalski
147
+ #
148
+ # Check a post for a certain rating. This is rather meant to be used via
149
+ # alias methids.
150
+ #
151
+ # @return [TrueClass]
152
+ # @return [FalseClass]
153
+ def test_rating(rating = nil)
154
+ rating ||= __callee__.to_s.sub(/\?/, '').to_sym
155
+ @rating == rating
156
+ end
157
+ alias safe? test_rating
158
+ alias questionable? test_rating
159
+ alias explicit? test_rating
160
+
161
+ # @author Maxine Michalski
162
+ #
163
+ # Comparison method for posts
164
+ #
165
+ # @return [TrueClass, FalseClass]
166
+ def ==(other)
167
+ other.is_a?(Post) && @id == other.id
168
+ end
169
+
170
+ private
171
+
172
+ # @author Maxine Michalski
173
+ #
174
+ # Helper function, to setup post file data
175
+ def setup_file_data(post)
176
+ @md5 = post[:md5]
177
+ @image = E621::Image.new(url: post[:file_url], ext: post[:file_ext],
178
+ width: post[:width], height: post[:height])
179
+ setup_sample(post)
180
+ setup_preview(post)
181
+ end
182
+
183
+ # @author Maxine Michalski
184
+ #
185
+ # Helper function, to setup post sample data
186
+ def setup_sample(post)
187
+ @sample = E621::Sample.new(url: post[:sample_url], ext: post[:file_ext],
188
+ width: post[:sample_width],
189
+ height: post[:sample_height])
190
+ end
191
+
192
+ # @author Maxine Michalski
193
+ #
194
+ # Helper function, to setup post preview data
195
+ def setup_preview(post)
196
+ @preview = E621::Preview.new(url: post[:preview_url], ext: 'jpg',
197
+ width: post[:preview_width],
198
+ height: post[:preview_height])
199
+ end
200
+
201
+ # @author Maxine Michalski
202
+ #
203
+ # Helper function to add additional post data
204
+ def setup_additional_data(post)
205
+ @sources = post[:source].nil? ? [] : post[:sources]
206
+ @artists = post[:artist].map { |a| Artist.new(a) }
207
+ @description = post[:description]
208
+ @favorites = post[:fav_count].to_i
209
+ @score = post[:score].to_i
210
+ @rating = case post[:rating]
211
+ when 's' then :safe
212
+ when 'q' then :questionable
213
+ when 'e' then :explicit
214
+ end
215
+ end
216
+
217
+ # @author Maxine Michalski
218
+ #
219
+ # Helper function to related posts (children, parent)
220
+ def setup_relatives(post)
221
+ @children = post[:children].split(',').map { |i| Post.new(i) }
222
+ @parent = post[:parent_id].nil? ? nil : Post.new(post[:parent_id])
223
+ end
224
+
225
+ # @author Maxine Michalski
226
+ #
227
+ # Helper function to set author of a post
228
+ def setup_author(post)
229
+ @author = User.new(id: post[:creator_id], name: post[:author])
230
+ end
231
+ end
232
+
233
+ # This class holds post information, fetched from e621, and gives access in a
234
+ # more Ruby like manner.
235
+ # This is only for deleted posts.
236
+ #
237
+ # @author Maxine Michalski
238
+ # @since 1.0.0
239
+ class DeletedPost < Post
240
+ # @return [String] deletion reason, when available
241
+ attr_reader :reason
242
+
243
+ # @author Maxine Michalski
244
+ #
245
+ # Initializer for DeletedPost.
246
+ #
247
+ # @param id [Integer] post id on e621
248
+ # @param delreason [String] reason of deletion
249
+ #
250
+ # @return the object
251
+ def initialize(id, delreason)
252
+ super(id)
253
+ @reason = delreason
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,99 @@
1
+ # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
2
+ #
3
+ # This file is part of rubyhexagon.
4
+ #
5
+ # rubyhexagon is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # rubyhexagon is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module E621
18
+ # Module to hold search classes
19
+ #
20
+ # @author Maxine Michalski
21
+ # @since 1.0.0
22
+ module Search
23
+ # Class to hold methods for Post searches.
24
+ #
25
+ # @author Maxine Michalski
26
+ # @since 1.0.0
27
+ class Posts
28
+ # @author Maxine Michalski
29
+ #
30
+ # Retrieve a list of deleted post, with deletion reason. Only id and
31
+ # del_reason is present.
32
+ # This method accepts blocks and only returns one page without blocks
33
+ #
34
+ # @param page [Integer] page of deleted posts to return
35
+ #
36
+ # @return [Array<Post>] an array of deleted posts
37
+ def self.deleted(page = 1)
38
+ d = fetch_deleted(page)
39
+ while block_given? && d != []
40
+ d.each { |del| yield del }
41
+ d = fetch_deleted(page)
42
+ page += 1
43
+ end
44
+ d
45
+ end
46
+
47
+ # @author Maxine Michalski
48
+ #
49
+ # Retrieve a list of post data, which is filtered by arguments.
50
+ # This method requires a block to be passed to it!
51
+ #
52
+ # @param tags [String] this can be any request that can be done on e621
53
+ # too
54
+ # @param limit [Integer] number of posts to return. This has a hard limit
55
+ # of 320
56
+ # @param before_id [Integer] start post ID is lesser than this ID
57
+ #
58
+ # This method accepts blocks and only returns one page without blocks
59
+ #
60
+ # @return [Array<Post>] an array of posts
61
+ def self.list(tags, limit = 320, before_id = 2_000_000)
62
+ parameters = { tags: tags, limit: limit, before_id: before_id }
63
+ posts = fetch_posts(parameters)
64
+ while block_given? && posts != []
65
+ posts.each { |post| yield post }
66
+ posts = fetch_posts(parameters)
67
+ parameters[:before_id] = posts.last.nil? ? 0 : posts.last.id
68
+ end
69
+ posts
70
+ end
71
+
72
+ # @author Maxine Michalski
73
+ #
74
+ # Helper function for deleted posts
75
+ #
76
+ # @return [Array<Post>] array of deleted posts
77
+ def self.fetch_deleted(page)
78
+ api = API.new
79
+ api.fetch('post', 'deleted_index', page: page).map do |post|
80
+ DeletedPost.new(post[:id], post[:delreason])
81
+ end
82
+ end
83
+ private_class_method :fetch_deleted
84
+
85
+ # @author Maxine Michalski
86
+ #
87
+ # Helper function for posts
88
+ #
89
+ # @return [Array<Post>] array of posts
90
+ def self.fetch_posts(parameters)
91
+ api = API.new
92
+ api.fetch('post', 'index', parameters).map do |data|
93
+ Post.new(data[:id]).show(data)
94
+ end
95
+ end
96
+ private_class_method :fetch_deleted
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
2
+ #
3
+ # This file is part of rubyhexagon.
4
+ #
5
+ # rubyhexagon is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # rubyhexagon is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module E621
18
+ # Module to hold search classes
19
+ #
20
+ # @author Maxine Michalski
21
+ # @since 1.0.0
22
+ module Search
23
+ # Class to hold methods for Tag searches.
24
+ #
25
+ # @author Maxine Michalski
26
+ # @since 1.0.0
27
+ class Tags
28
+ # @author Maxine Michalski
29
+ #
30
+ # Retrieve a list of post data, which is filtered by arguments.
31
+ # This method accepts a block and returns only one page without one.
32
+ #
33
+ # @param name [String] name pattern to search tags with
34
+ # @param limit [Integer] number of tags to return. This has a hard limit
35
+ # of 500
36
+ # @param after_id [Integer] only look for tags after this ID
37
+ #
38
+ # @return [Array<Tag>] array of tags
39
+ def self.list(name, limit = 500, page = 1)
40
+ params = { name_pattern: name, order: :date, limit: limit,
41
+ page: page }
42
+ t = fetch_tags(params)
43
+ while block_given? && t != []
44
+ t.each { |tag| yield tag }
45
+ t = fetch_tags(params)
46
+ params[:page] += 1
47
+ end
48
+ t
49
+ end
50
+
51
+ # @author Maxine Michalski
52
+ #
53
+ # Helper function to get Tag data
54
+ #
55
+ # @return [Array<Tag>] array of tags
56
+ def self.fetch_tags(params)
57
+ api = API.new
58
+ api.fetch('tag', 'index', params).map do |data|
59
+ Tag.new(data[:id]).show(data)
60
+ end
61
+ end
62
+ private_class_method :fetch_tags
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,89 @@
1
+ # Copyright 2014-2018 Maxine Michalski <maxine@furfind.net>
2
+ #
3
+ # This file is part of rubyhexagon.
4
+ #
5
+ # rubyhexagon is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # rubyhexagon is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module E621
18
+ # Class to hold tag information.
19
+ #
20
+ # @author Maxine Michalski
21
+ # @since 1.0.0
22
+ class Tag
23
+ # @return [Integer] id of tag
24
+ attr_reader :id
25
+
26
+ # @return [String] name of tag
27
+ attr_reader :name
28
+
29
+ # @return [Integer] number of posts that have this tag assigned to them
30
+ attr_reader :count
31
+
32
+ # @return [Type] type of this tag
33
+ attr_reader :type
34
+
35
+ # @author Maxine Michalski
36
+ #
37
+ # Initializer for Tag.
38
+ #
39
+ # @raise InvalidIDError
40
+ #
41
+ # @param tag [Integer] tag data, fetched from e621
42
+ #
43
+ # @return the object
44
+ def initialize(id)
45
+ unless id.is_a?(Integer) && id > 0
46
+ raise InvalidIDError, "ID out of range: #{id}"
47
+ end
48
+ @id = id
49
+ end
50
+
51
+ # @author Maxine Michalski
52
+ #
53
+ # Bang method to fill tag data. If data is already available at creation,
54
+ # use the initializer!
55
+ def show!(tag = nil)
56
+ tag = API.new.fetch('tag', 'show', id: @id) if tag.nil?
57
+ @name = tag[:name]
58
+ @count = tag[:count].to_i
59
+ @type = (Type.new(tag[:type], tag[:type_locked]) unless tag[:type].nil?)
60
+ end
61
+
62
+ # @author Maxine Michalski
63
+ #
64
+ # Bang method to fill tag data. If data is already available at creation,
65
+ # use the initializer!
66
+ def show(tag = nil)
67
+ new_tag = Tag.new(@id)
68
+ new_tag.show!(tag)
69
+ new_tag
70
+ end
71
+
72
+ # @author Maxine Michalski
73
+ #
74
+ # Comparison operator for Tag, to override the default one.
75
+ # @note This method looks for same id and same name only.
76
+ # @note If there is only a an ID set with one argument, then only compare
77
+ # IDs
78
+ #
79
+ # @return [TrueClass, FalseClass]
80
+ def ==(other)
81
+ return false unless other.is_a?(Tag)
82
+ if @name.nil? || other.name.nil?
83
+ @id == other.id
84
+ else
85
+ @id == other.id && @name == other.name
86
+ end
87
+ end
88
+ end
89
+ end