Skip to content

DSL refactor and minor Ruby improvements and fixes #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ inherit_from: .rubocop_todo.yml
require: rubocop-rspec

AllCops:
TargetRubyVersion: 2.3.0
Include:
- 'lib/**/*'
Exclude:
Expand All @@ -13,6 +14,10 @@ AllCops:
- 'Gemfile'
DisplayCopNames: true

# Ruby 2.3+
FrozenStringLiteralComment:
Enabled: false

# My personal style
# -----------------

Expand Down
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.3.0
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
cache: bundler
language: ruby
rvm:
- "2.1"
- "2.2"
- "2.3"
- "2.4"
- "2.5"
- jruby-9.1.13.0
install:
- bundle install --retry=3
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ gemspec

group :test do
gem 'coveralls', require: false
gem 'saharspec', '= 0.0.4'
gem 'saharspec', '0.0.4'
end
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
[![Build Status](https://travis-ci.org/molybdenum-99/tlaw.svg?branch=master)](https://travis-ci.org/molybdenum-99/tlaw)
[![Coverage Status](https://coveralls.io/repos/molybdenum-99/tlaw/badge.svg?branch=master)](https://coveralls.io/r/molybdenum-99/tlaw?branch=master)

**TLAW** (pronounce it like "tea+love"... or whatever) is the last (and
only) API wrapper framework for _get-only APIes_<sup>[*](#get-only-api)</sup>
(think weather, search, economical indicators, geonames and so on).
**TLAW** (pronounce it like "tea+love"... or whatever) is the last (and only)
API wrapper framework you'll ever need for accessing _GET-only APIs_<sup>[*](#get-only-api)</sup>
in a consistent way (think weather, search, economical indicators, geonames and so on).

## Table Of Contents

Expand Down Expand Up @@ -302,7 +302,7 @@ post_process_items('foo') {

# More realistic examples:
post_process('meta.count', &:to_i)
post_process('daily') {
post_process_items('daily') {
post_process('date', &Date.method(:parse))
}
post_process('auxiliary_value') { nil } # Nil's will be thrown away completely
Expand All @@ -314,9 +314,9 @@ See full post-processing features descriptions in
#### All at once

All described response processing steps are performed in this order:

* parsing and initial flattening of JSON (or XML) hash;
* applying post-processors (and flatten the response after _each_ of
them);
* applying post-processors (and flatten the response after _each_ of them);
* make `DataTable`s from arrays of hashes.

### Documentability
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ require 'rubygems/tasks'
Gem::Tasks.new

namespace :doc do
desc "Prints TOC for README.md (doesn't inserts it automatically!)"
desc "Prints TOC for README.md (doesn't insert it automatically!)"
task :toc do
# NB: really dumb. Yet better than any Solution I can find :(
# Grabbing it from project to project.
Expand Down
4 changes: 2 additions & 2 deletions examples/demo_base.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'pp'
$:.unshift File.expand_path('../../lib', __FILE__)

$:.unshift 'lib'
require 'tlaw'
require 'pp'

begin
require 'dotenv'
Expand Down
16 changes: 2 additions & 14 deletions lib/tlaw.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
require 'backports/2.5.0/kernel/yield_self'
require 'open-uri'
require 'json'
require 'addressable/uri'
require 'addressable/template'

# Let no one know! But they in Ruby committee just too long to add
# something like this to the language.
#
# See also https://bugs.ruby-lang.org/issues/12760
#
# @private
class Object
def derp
yield self
end
end

# TLAW is a framework for creating API wrappers for get-only APIs (like
# weather, geonames and so on) or subsets of APIs (like getting data from
# Twitter).
Expand Down Expand Up @@ -55,8 +44,7 @@ module TLAW
require_relative 'tlaw/util'
require_relative 'tlaw/data_table'

require_relative 'tlaw/param'
require_relative 'tlaw/param_set'
require_relative 'tlaw/params'

require_relative 'tlaw/api_path'
require_relative 'tlaw/endpoint'
Expand Down
9 changes: 5 additions & 4 deletions lib/tlaw/api_path.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require_relative 'params/set'
require 'forwardable'

module TLAW
Expand Down Expand Up @@ -54,7 +55,7 @@ def inherit(namespace, **attrs)

# @private
def params_from_path!
Addressable::Template.new(path).keys.each do |key| # rubocop:disable Performance/HashEachMethods
Addressable::Template.new(path).keys.each do |key|
param_set.add key.to_sym, keyword: false
end
end
Expand All @@ -67,13 +68,13 @@ def setup_parents(parent)

# @private
def symbol=(sym)
@symbol = sym
@path ||= "/#{sym}"
@symbol = sym
end

# @return [ParamSet]
# @return [Params::Set]
def param_set
@param_set ||= ParamSet.new
@param_set ||= Params::Set.new
end

# @private
Expand Down
4 changes: 2 additions & 2 deletions lib/tlaw/data_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ def inspect
end

# @private
def pretty_print(pp)
pp.text("#<#{self.class.name}[#{keys.join(', ')}] x #{size}>")
def pretty_print(printer)
printer.text("#<#{self.class.name}[#{keys.join(', ')}] x #{size}>")
end
end
end
116 changes: 7 additions & 109 deletions lib/tlaw/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ module DSL
# API query string (it would be `name` by default).
# @option opts [#to_proc] :format How to format this option before
# including into URL. By default, it is just `.to_s`.
# @option opts [String] :desc Param description. You could do it
# @option opts [String] :desc Params::Base description. You could do it
# multiline and with indents, like {#desc}.
# @option opts :default Default value for this param. Would be
# rendered in method definition and then passed to target API
Expand Down Expand Up @@ -399,113 +399,11 @@ module DSL
# ```
#
# See also {#post_process} for some generic explanation of post-processing.

# @private
class BaseWrapper
def initialize(object)
@object = object
end

def define(&block)
instance_eval(&block)
end

def description(text)
# first, remove spaces at a beginning of each line
# then, remove empty lines before and after docs block
@object.description =
text
.gsub(/^[ \t]+/, '')
.gsub(/\A\n|\n\s*\Z/, '')
end

alias_method :desc, :description

def docs(link)
@object.docs_link = link
end

def param(name, type = nil, **opts)
@object.param_set.add(name, **opts.merge(type: type))
end

def post_process(key = nil, &block)
@object.response_processor.add_post_processor(key, &block)
end

def post_process_replace(&block)
@object.response_processor.add_replacer(&block)
end

class PostProcessProxy
def initialize(parent_key, parent)
@parent_key = parent_key
@parent = parent
end

def post_process(key = nil, &block)
@parent.add_item_post_processor(@parent_key, key, &block)
end
end

def post_process_items(key, &block)
PostProcessProxy
.new(key, @object.response_processor)
.instance_eval(&block)
end
end

# @private
class EndpointWrapper < BaseWrapper
end

# @private
class NamespaceWrapper < BaseWrapper
def endpoint(name, path = nil, **opts, &block)
update_existing(Endpoint, name, path, **opts, &block) ||
add_child(Endpoint, name, path: path || "/#{name}", **opts, &block)
end

def namespace(name, path = nil, &block)
update_existing(Namespace, name, path, &block) ||
add_child(Namespace, name, path: path || "/#{name}", &block)
end

private

WRAPPERS = {
Endpoint => EndpointWrapper,
Namespace => NamespaceWrapper
}.freeze

def update_existing(child_class, name, path, **opts, &block)
existing = @object.children[name] or return nil
existing < child_class or
fail ArgumentError, "#{name} is already defined as #{child_class == Endpoint ? 'namespace' : 'endpoint'}, you can't redefine it as #{child_class}"

!path && opts.empty? or
fail ArgumentError, "#{child_class} is already defined, you can't change its path or options"

WRAPPERS[child_class].new(existing).define(&block) if block
end

def add_child(child_class, name, **opts, &block)
@object.add_child(
child_class.inherit(@object, symbol: name, **opts)
.tap { |c| c.setup_parents(@object) }
.tap(&:params_from_path!)
.tap { |c|
WRAPPERS[child_class].new(c).define(&block) if block
}
)
end
end

# @private
class APIWrapper < NamespaceWrapper
def base(url)
@object.base_url = url
end
end
end
end

require_relative 'dsl/api_wrapper'
require_relative 'dsl/base_wrapper'
require_relative 'dsl/endpoint_wrapper'
require_relative 'dsl/namespace_wrapper'
require_relative 'dsl/post_process_proxy'
11 changes: 11 additions & 0 deletions lib/tlaw/dsl/api_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require_relative 'namespace_wrapper'

module TLAW
module DSL
class APIWrapper < NamespaceWrapper
def base(url)
@object.base_url = url
end
end
end
end
48 changes: 48 additions & 0 deletions lib/tlaw/dsl/base_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require_relative 'post_process_proxy'

module TLAW
module DSL
class BaseWrapper
def initialize(object)
@object = object
end

def define(&block)
instance_eval(&block)
end

def description(text)
# first, remove spaces at a beginning of each line
# then, remove empty lines before and after docs block
@object.description =
text
.gsub(/^[ \t]+/, '')
.gsub(/\A\n|\n\s*\Z/, '')
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last thing that I noticed: let's probably remove this logic completely? It just repeats what Ruby 2.3 "squiggly heredoc" do, and as this PR specifies 2.3 as the minimum version, there is no need to replicating language features. One can just

  description <<~D
    Any description
    goes here.

    Like a boss!
  D

...and be happy.


alias_method :desc, :description

def docs(link)
@object.docs_link = link
end

def param(name, type = nil, **opts)
@object.param_set.add(name, **opts.merge(type: type))
end

def post_process(key = nil, &block)
@object.response_processor.add_post_processor(key, &block)
end

def post_process_replace(&block)
@object.response_processor.add_replacer(&block)
end

def post_process_items(key, &block)
PostProcessProxy
.new(key, @object.response_processor)
.instance_eval(&block)
end
end
end
end
8 changes: 8 additions & 0 deletions lib/tlaw/dsl/endpoint_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require_relative 'base_wrapper'

module TLAW
module DSL
class EndpointWrapper < BaseWrapper
end
end
end
Loading