greentext 0.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 219465b29edc32e5ba85d822d9b08f353a559808
4
- data.tar.gz: 48a6b15ddea763362e67106088040dceaa4d5f95
3
+ metadata.gz: 70e6ce509e15bac2d2e63dad243d7ce386d7cffb
4
+ data.tar.gz: 8fa2485f164f343aa72540210285c378d33cc901
5
5
  SHA512:
6
- metadata.gz: 21abfe740a2cc63e9236cacf0a69abaab5dde707f88f802c3015bffaa2f953bc3a52ec3ce2d7296727f4d9b7c47ab546cd0f77dfbb3e703f9ec2166efbba4969
7
- data.tar.gz: 3c3899bc4bd78d75801f81d2bdcd0b028316efe259293d41eacb99f43ec086682056a25d19a951df78d5bb174b6e0c079bf1a8ed3511c232bbf67d3eea50eec9
6
+ metadata.gz: 08ac112408c79420c2cda4af93caeca85ea4ee1587e06f4dd517db646b36d7e2cfe5203b3f3370aead01288213fdc15f939b5129da79cb2defd3a0c58ad430d9
7
+ data.tar.gz: 603b286fe5603f64b871717a3b944fdf3843318f0763af5b9036c94305935374927b78b43a52a9dc320d117155b167458d8638447bd54b0cdc4154aa41857d5b
data/.gitignore CHANGED
@@ -7,6 +7,7 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
+ spec/cassettes/
10
11
  doc/
11
12
  lib/bundler/man
12
13
  pkg
data/README.md CHANGED
@@ -1,19 +1,16 @@
1
- **Greentext** *Unofficial 4chan API.*
2
- By Avinash Dwarapu
1
+ **Greentext** *Unofficial 4chan API Client.*
3
2
 
4
- **
5
- [Usage](#usage) |
6
- [Installation](#installation) |
7
- [Contributing](#contributing)
8
- **
3
+ [![Gem Version](https://badge.fury.io/rb/greentext.svg)](http://badge.fury.io/rb/greentext)
4
+ [![Dependency Status](https://gemnasium.com/avidw/greentext.svg)](https://gemnasium.com/avidw/greentext)
5
+ [![Build Status](https://travis-ci.org/avidw/greentext.svg)](https://travis-ci.org/avidw/greentext)
9
6
 
10
- ## Usage
11
- ### Greentext::Client
12
- Haven't implemented it yet :neutral_face:
7
+ ---
13
8
 
9
+ ## Usage
14
10
  ### Greentext::Dirty
15
- Aside from the main implementation, there is also a quick
16
- way of getting data from the API using `Greentext::Dirty`.
11
+
12
+ I'm still working on the main implementation. Meanwhile, one
13
+ way of getting data from the API is using `Greentext::Dirty`.
17
14
 
18
15
  ```ruby
19
16
  require "greentext/dirty"
@@ -35,11 +32,37 @@ thread.first[:url]
35
32
  # => "http://boards.4chan.org/b/res/543125139#p543128395"
36
33
  ```
37
34
 
35
+ ### Greentext::Client
36
+
37
+ Haven't implemented it yet :neutral_face:.
38
+ Meanwhile, use `Greentext::Dirty`.
39
+
40
+ ```ruby
41
+ require "greentext"
42
+
43
+ client = Greentext::Client.new
44
+ # => #<Greentext::Client:0x007f9f9a624260 @base_uri="http://a.4cdn.org/">
45
+
46
+ cg = client.boards.first
47
+ # => #<Greentext::Client::Board:0x007f9f9aa10928 @attrs={"board"=>"3", "title"=>"3DCG", "ws_board"=>1, ... }>
48
+ cg.title
49
+ # => "3DCG"
50
+ cg.ws_board?
51
+ # => true
52
+
53
+ post = cg.threads.first
54
+ # => #<Greentext::Client::Post:0x005f4f9aa12393 @attrs={"no"=>240555661, "now"=>"04\/21\/14(Mon)13:26:27", ... }>
55
+ post.time
56
+ # => #<DateTime: 2014-04-21T18:24:10+00:00 ((2456769j,66250s,699039399n),+0s,2299161j)>
57
+ post.comment
58
+ # => "Love it. Looks like there&#039;s going to be ton of secret ... "
59
+ ```
60
+
38
61
  ## Installation
39
62
 
40
63
  Add this line to your application's Gemfile:
41
64
 
42
- gem 'greentext'
65
+ gem "greentext"
43
66
 
44
67
  And then execute:
45
68
 
@@ -53,6 +76,18 @@ Or install it yourself as:
53
76
 
54
77
  1. Fork it ( http://github.com/avidw/greentext/fork )
55
78
  2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Install development requirements (`bundle install`)
56
80
  3. Commit your changes (`git commit -am 'Add some feature'`)
57
81
  4. Push to the branch (`git push origin my-new-feature`)
58
82
  5. Create new Pull Request
83
+
84
+ ---
85
+
86
+ This project follows the [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide)
87
+ aside from a couple of differences:
88
+
89
+ - Documented using TomDoc instead of RDoc.
90
+ - Double quotes are always preferred over single quotes unless escaping them becomes a nightmare.
91
+ - There are no spaces around `=` for default arguments in a method. This is just a personal thing.
92
+
93
+ Double check the code using `bundle exec rubocop` before submitting a pull request.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "greentext"
5
- s.version = "0.1.0"
5
+ s.version = "0.2.0"
6
6
  s.authors = ["Avinash Dwarapu"]
7
7
  s.email = ["d.nash.avi@gmail.com"]
8
8
  s.summary = %q{Unofficial API for 4chan.}
@@ -15,9 +15,10 @@ Gem::Specification.new do |s|
15
15
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
16
  s.require_paths = ["lib"]
17
17
 
18
+ s.add_dependency "bundler", "~> 1.6"
19
+ s.add_dependency "memoizable", "~> 0.4"
18
20
  s.add_dependency "faraday", "~> 0.9"
19
- s.add_dependency "memoizable", "~> 0.4.2"
20
- s.add_dependency "oj"
21
+ s.add_dependency "faraday_middleware", "~> 0.9"
21
22
 
22
23
  s.add_development_dependency "rubocop"
23
24
  s.add_development_dependency "rspec"
@@ -0,0 +1,25 @@
1
+ require "greentext/client"
2
+
3
+ # Public: The main Greentext module.
4
+ module Greentext
5
+ class << self
6
+ # Public: An instance of the client when calling methods
7
+ # on the Greentext module.
8
+ #
9
+ # Returns an Instance of Greentext::Client
10
+ def client
11
+ @client ||= Greentext::Client.new
12
+ end
13
+
14
+ def respond_to?(method_name, include_private=false)
15
+ client.respond_to?(method_name, include_private) || super
16
+ end
17
+
18
+ private
19
+
20
+ def method_missing(method_name, *args, &block)
21
+ return super unless client.respond_to?(method_name)
22
+ client.send(method_name, *args, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ require "memoizable"
2
+ require "time"
3
+
4
+ module Greentext
5
+ # Internal: The base class for the models.
6
+ class Base
7
+ include Memoizable
8
+ attr_reader :attrs
9
+ alias_method :to_h, :attrs
10
+
11
+ class << self
12
+ # Internal: Dynamically define an attribute method for a given attribute
13
+ #
14
+ # key - A Symbol of the method name.
15
+ # value - An optional Symbol of the attribute key.
16
+ #
17
+ # Yields an optional attribute key.
18
+ #
19
+ # Returns nothing.
20
+ def attr_reader(key, value=key)
21
+ define_method(key) { @attrs[value.to_s] }
22
+ memoize(key)
23
+ end
24
+
25
+ # Internal: Dynamically define a predicate method for a given attribute.
26
+ #
27
+ # key - A Symbol of the method name.
28
+ # value - An optional Symbol of the attribute key.
29
+ #
30
+ # Returns nothing.
31
+ def attr_predicate(key, value=key)
32
+ define_method(:"#{key}?") { @attrs[value.to_s] == 1 }
33
+ memoize(:"#{key}?")
34
+ end
35
+
36
+ # Internal: Dynamically define an date method for a given attribute.
37
+ #
38
+ # key - A Symbol of the method name.
39
+ # value - An optional Symbol of the attribute key.
40
+ #
41
+ # Returns nothing.
42
+ def attr_epoch(key, value=key)
43
+ define_method(key) { DateTime.strptime(@attrs[value.to_s].to_s, "%s") }
44
+ memoize(key)
45
+ end
46
+ end
47
+
48
+ # Internal: Initialize a new Object with given attributes.
49
+ #
50
+ # attrs - An optional Hash of attributes.
51
+ #
52
+ # Returns an instance of the Object
53
+ def initialize(attrs)
54
+ @attrs = attrs
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,12 @@
1
+ require "greentext/base"
2
+
3
+ module Greentext
4
+ # Public: (Model) A 4chan board.
5
+ class Board < Greentext::Base
6
+ attr_reader :name, :board
7
+ attr_reader :title
8
+ attr_reader :posts_per_page, :per_page
9
+ attr_reader :pages
10
+ attr_predicate :worksafe, :ws_board
11
+ end
12
+ end
@@ -0,0 +1,53 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+
4
+ require "greentext/client/board"
5
+ require "greentext/client/post"
6
+
7
+ module Greentext
8
+ # Public: The Client to access the API
9
+ #
10
+ # Examples
11
+ # client = Greentext::Client.new
12
+ # client.boards
13
+ # # => [#<Greentext::Board>, ...
14
+ class Client
15
+ include Greentext::Client::Board
16
+ include Greentext::Client::Post
17
+
18
+ attr_accessor :api_endpoint
19
+
20
+ def initialize
21
+ yield self if block_given?
22
+ end
23
+
24
+ # Public: Get the API Endpoint
25
+ #
26
+ # Returns a String containing a URI.
27
+ def api_endpoint
28
+ @api_endpoint ||= "http://a.4cdn.org/"
29
+ end
30
+
31
+ private
32
+
33
+ # Private: Return the current Faraday connection or create a new one.
34
+ #
35
+ # Returns a Faraday session.
36
+ def connection
37
+ @connection ||= Faraday.new(url: api_endpoint) do |conn|
38
+ conn.response :json
39
+ conn.adapter Faraday.default_adapter
40
+ end
41
+ end
42
+
43
+ # Private: Send a GET request to the path provided.
44
+ #
45
+ # path - The path String under the base url to access.
46
+ # params - An optional params Hash.
47
+ #
48
+ # Returns a Faraday Response object.
49
+ def get(path, params={})
50
+ connection.get(path, params).body
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,27 @@
1
+ require "greentext/board"
2
+
3
+ module Greentext
4
+ class Client
5
+ # Internal: Methods for interacting with boards.
6
+ module Board
7
+ # Public: List all the boards available.
8
+ #
9
+ # Returns an Array of Greentext::Board instances.
10
+ def boards
11
+ boards = get("/boards.json")["boards"]
12
+ boards.map { |board| Greentext::Board.new(board) }
13
+ end
14
+
15
+ # Public: List all available threads in a board.
16
+ #
17
+ # board - A Greentext::Board object.
18
+ #
19
+ # Returns an Array of Greentext::Post instances.
20
+ def threads(board)
21
+ get("/#{board.name}/catalog.json")
22
+ .map { |page| page["threads"] }.flatten
23
+ .map { |thread| Greentext::Post.new(thread.merge("board" => board)) }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require "greentext/post"
2
+
3
+ module Greentext
4
+ class Client
5
+ # Internal: Methods for interacting with posts
6
+ module Post
7
+ # Public: Get the replies to a Greentext::Post object.
8
+ # This will only work if the post is OP.
9
+ #
10
+ # post - a Greentext::Post object.
11
+ #
12
+ # Returns an Array of Greentext::Post instances.
13
+ def expand(post)
14
+ if post.op?
15
+ posts = get("/#{post.board.name}/res/#{post.no}.json")["posts"]
16
+ posts.shift # Remove OP's post
17
+ posts.map do |params|
18
+ params.merge!("board" => post.board)
19
+ Greentext::Post.new(params)
20
+ end
21
+ else
22
+ raise ArgumentError, "This is not the first post in the thread."
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,15 +1,9 @@
1
1
  require "faraday"
2
- require "cgi"
3
- require "oj"
2
+ require "json"
4
3
 
5
4
  module Greentext
6
5
  # Public: Crudely written but quick way to access the 4chan API.
7
6
  # Surprisingly, Rubocop's pretty okay with this mess.
8
- #
9
- # Examples
10
- #
11
- # Greentext.new.threads(:b)
12
- # # => [{"no" => 543103136, "last_modified" => 1398012243}, ...
13
7
  class Dirty
14
8
  def boards
15
9
  get("/boards.json")["boards"]
@@ -18,33 +12,25 @@ module Greentext
18
12
  def threads(board)
19
13
  get("/#{board}/threads.json")
20
14
  .map { |page| page["threads"] }.flatten
21
- .map { |post| Hash[post.map { |k, v| [k.to_sym, v] }] }
15
+ .map { |thread| symbolize(thread) }
22
16
  end
23
17
  alias_method :board, :threads
24
18
 
25
19
  def posts(board, thread)
26
20
  get("/#{board}/res/#{thread}.json")["posts"]
27
- .sort_by { |h| h["resto"] }
28
- .map do |post|
29
- post.merge!("time" => DateTime.strptime(post["time"].to_s, "%s"))
30
- post.merge!("name" => nil) if post["name"] == "Anonymous"
31
- post["url"] =
32
- "http://boards.4chan.org/#{board}/res/" \
33
- "#{post['resto'] == 0 ? post['no'] : post['resto']}#p#{post['no']}"
34
- Hash[post.map { |k, v| [k.to_sym, v] }]
35
- end
21
+ .map { |post| symbolize(post) }
36
22
  end
37
23
  alias_method :thread, :posts
38
24
 
39
25
  private
40
26
 
41
27
  def get(path)
42
- res = Faraday.new(url: "http://a.4cdn.org/").get(path)
43
- if res.headers["Content-Type"] == "application/json"
44
- Oj.load(res.body)
45
- else
46
- res
47
- end
28
+ res = Faraday.new(url: "http://a.4cdn.org/").get(path).body
29
+ JSON.parse(res) if res.length > 2
30
+ end
31
+
32
+ def symbolize(hash)
33
+ Hash[hash.map { |k, v| [k.to_sym, v] }]
48
34
  end
49
35
  end
50
36
  end
@@ -0,0 +1,66 @@
1
+ require "greentext/base"
2
+
3
+ module Greentext
4
+ # Public: (Model) A 4chan post in a thread.
5
+ class Post < Greentext::Base
6
+ attr_reader :no
7
+ attr_reader :resto
8
+ attr_reader :board
9
+
10
+ attr_epoch :time
11
+ attr_epoch :last_modified
12
+
13
+ attr_reader :trip
14
+ attr_reader :id
15
+ attr_reader :country
16
+ attr_reader :country_name
17
+ attr_reader :email
18
+ attr_reader :subject, :sub
19
+ attr_reader :comment, :com
20
+ attr_reader :custom_spoiler
21
+ attr_reader :omitted_posts
22
+ attr_reader :omitted_images
23
+ attr_reader :replies
24
+ attr_reader :images
25
+ attr_reader :capcode_replies
26
+ attr_reader :tag
27
+ attr_reader :semantic_url
28
+
29
+ attr_predicate :sticky
30
+ attr_predicate :closed
31
+ attr_predicate :deleted
32
+ attr_predicate :spoiler
33
+ attr_predicate :bump_limit_reached, :bumplimit
34
+ attr_predicate :image_limit_reached, :imagelimit
35
+
36
+ attr_predicate :image, :filename
37
+
38
+ # Public: Get information about the image attached to the post.
39
+ #
40
+ # Returns a Hash.
41
+ def image
42
+ {
43
+ width: @attrs['w'],
44
+ height: @attrs['h'],
45
+ filename: @attrs['tim'],
46
+ md5: @attrs['md5'],
47
+ url: "https://i.4cdn.org/#{@attrs['board']}/#{@attrs['tim']}.#{@attrs['ext']}"
48
+ }
49
+ end
50
+
51
+ # Public: Get information about the thumbnail of the image.
52
+ #
53
+ # Returns a Hash.
54
+ def thumbnail
55
+ { width: @attrs["tn_w"], height: @attrs["tn_h"],
56
+ url: "https://t.4cdn.org/#{@attrs['board']}/#{@attrs['tim']}s.jpg" }
57
+ end
58
+
59
+ # Public: Determines if the post is OP.
60
+ #
61
+ # Returns a Boolean value.
62
+ def op?
63
+ @attrs["resto"] == 0
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe Greentext::Board, :vcr do
4
+ describe "#boards" do
5
+ it "gets an Array of boards" do
6
+ expect(@client.boards).to be_an(Array)
7
+ end
8
+
9
+ it "converts objects to Greentext::Board instances" do
10
+ expect(@client.boards.sample).to be_an_instance_of(Greentext::Board)
11
+ end
12
+ end
13
+
14
+ describe "#threads" do
15
+ it "gets an Array of posts" do
16
+ expect(@client.threads(@client.boards.sample)).to be_an(Array)
17
+ end
18
+
19
+ it "converts objects to Greentext::Post instances" do
20
+ expect(@client.threads(@client.boards.sample).sample).to be_an_instance_of(Greentext::Post)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe Greentext::Client do
4
+ it "has the api url set correctly" do
5
+ expect(@client.api_endpoint).to include("a.4cdn.org")
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require "spec_helper"
@@ -3,6 +3,7 @@ require "webmock/rspec"
3
3
  require "vcr"
4
4
 
5
5
  VCR.configure do |c|
6
+ c.default_cassette_options = {record: :new_episodes}
6
7
  c.cassette_library_dir = "spec/cassettes"
7
8
  c.hook_into :webmock
8
9
  end
@@ -13,11 +14,16 @@ RSpec.configure do |c|
13
14
  c.filter_run :focus
14
15
  c.order = "random"
15
16
 
17
+ c.before :all do
18
+ @client = Greentext.client
19
+ end
20
+
16
21
  c.around(:each, :vcr) do |example|
17
22
  name = example.metadata[:full_description]
18
23
  .split(/\s+/, 2)
19
24
  .join("/")
20
- .underscore
25
+ .downcase
26
+ .tr(' ', '_')
21
27
  .gsub(/[^\w\/]+/, "_")
22
28
  VCR.use_cassette(name) { example.call }
23
29
  end
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: greentext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avinash Dwarapu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-20 00:00:00.000000000 Z
11
+ date: 2014-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: faraday
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.9'
19
+ version: '1.6'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.9'
26
+ version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: memoizable
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.4.2
33
+ version: '0.4'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.4.2
40
+ version: '0.4'
41
41
  - !ruby/object:Gem::Dependency
42
- name: oj
42
+ name: faraday
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.9'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday_middleware
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rubocop
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -139,8 +153,16 @@ files:
139
153
  - Rakefile
140
154
  - greentext.gemspec
141
155
  - lib/greentext.rb
156
+ - lib/greentext/base.rb
157
+ - lib/greentext/board.rb
142
158
  - lib/greentext/client.rb
159
+ - lib/greentext/client/board.rb
160
+ - lib/greentext/client/post.rb
143
161
  - lib/greentext/dirty.rb
162
+ - lib/greentext/post.rb
163
+ - spec/board_spec.rb
164
+ - spec/client_spec.rb
165
+ - spec/post_spec.rb
144
166
  - spec/spec_helper.rb
145
167
  homepage: https://github.com/avidw/greentext
146
168
  licenses:
@@ -167,4 +189,7 @@ signing_key:
167
189
  specification_version: 4
168
190
  summary: Unofficial API for 4chan.
169
191
  test_files:
192
+ - spec/board_spec.rb
193
+ - spec/client_spec.rb
194
+ - spec/post_spec.rb
170
195
  - spec/spec_helper.rb