greentext 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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