Skip to content
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

Extract Response Processing #7

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
7 changes: 4 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ AllCops:
- 'Rakefile'
- '*.gemspec'
- 'Gemfile'
- '**/*.orig'
DisplayCopNames: true

# Ruby 2.3+
Expand Down Expand Up @@ -74,9 +75,9 @@ Metrics/LineLength:
Style/Documentation:
Enabled: false

Style/Lambda:
Exclude:
- 'lib/tlaw/response_processor.rb'
# Style/Lambda:
# Exclude:
# - 'lib/tlaw/processors/response_processor.rb'

# Those are just useless
# ----------------------
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

* Codebase modernized and rubocopped;
* Support for redirects;
* Support for pattern-based (regexp) postprocessors.
* Support for pattern-based (regexp) post-processors.

# 0.0.1 (2016-09-19)

Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,31 +281,31 @@ them in several ways:

```ruby
# input is entire response, block can mutate it
post_process { |hash| hash['foo'] = 'bar' }
transform { |hash| hash['foo'] = 'bar' }

# input is entire response, and response is fully replaced with block's
# return value
post_process { |hash| hash['foo'] } # Now only "foo"s value will be response
transform { |hash| hash['foo'] } # Now only "foo"s value will be response
Copy link
Contributor

Choose a reason for hiding this comment

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

transform(replace: true) should be here, right?..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@zverok i mentioned in another comment of yours that I'd like to remove the replace option and just always return the value that should be used rather than mutate the argument to the block. Thoughts?


# input is value of response's key "some_key", return value of a block
# becames new value of "some_key".
post_process('some_key') { |val| other_val }
transform('some_key') { |val| other_val }

# Post-processing each item, if response['foo'] is array:
post_process_items('foo') {
transform_items('foo') {
# mutate entire item
post_process { |item| item.delete('bar') }
transform { |item| item.delete('bar') }

# if item is a Hash, replace its "bar" value
post_process('bar') { |val| val.to_s }
transform('bar') { |val| val.to_s }
}

# More realistic examples:
post_process('meta.count', &:to_i)
post_process_items('daily') {
post_process('date', &Date.method(:parse))
transform('meta.count', &:to_i)
transform_items('daily') {
transform('date', &Date.method(:parse))
}
post_process('auxiliary_value') { nil } # Nil's will be thrown away completely
transform('auxiliary_value') { nil } # Nil's will be thrown away completely
```

See full post-processing features descriptions in
Expand Down
18 changes: 9 additions & 9 deletions examples/forecast_io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,20 @@ class ForecastIO < TLAW::API
}
end

post_process 'currently.time', &Time.method(:at)
transform 'currently.time', &Time.method(:at)

post_process_items('minutely.data') {
post_process 'time', &Time.method(:at)
transform_items('minutely.data') {
transform 'time', &Time.method(:at)
}

post_process_items('hourly.data') {
post_process 'time', &Time.method(:at)
transform_items('hourly.data') {
transform 'time', &Time.method(:at)
}

post_process_items('daily.data') {
post_process 'time', &Time.method(:at)
post_process 'sunriseTime', &Time.method(:at)
post_process 'sunsetTime', &Time.method(:at)
transform_items('daily.data') {
transform 'time', &Time.method(:at)
transform 'sunriseTime', &Time.method(:at)
transform 'sunsetTime', &Time.method(:at)
}
end
end
Expand Down
6 changes: 3 additions & 3 deletions examples/giphy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class Giphy < TLAW::API

param :phrase, field: :s, keyword: false, required: true, desc: 'Phrase to translate'

post_process(/\.(size|mp4_size|webp_size|width|height|frames)/, &:to_i)
transform(/\.(size|mp4_size|webp_size|width|height|frames)/, &:to_i)
end

endpoint :random do
Expand All @@ -62,8 +62,8 @@ class Giphy < TLAW::API
param :rating, desc: 'Parental advisory rating'
end

post_process_items('data') do
post_process(/\.(size|mp4_size|webp_size|width|height|frames)/, &:to_i)
transform_items('data') do
transform(/\.(size|mp4_size|webp_size|width|height|frames)/, &:to_i)
end
end
end
Expand Down
34 changes: 17 additions & 17 deletions examples/open_weather_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,52 +214,52 @@ class OpenWeatherMap < TLAW::API
param :lng, :to_f, required: true, desc: 'Longitude'
end

post_process { |e|
transform { |e|
e['city.coord'] = Geo::Coord.new(e['city.coord.lat'], e['city.coord.lon']) \
if e['city.coord.lat'] && e['city.coord.lon']
}
post_process('city.coord.lat') { nil }
post_process('city.coord.lon') { nil }
transform('city.coord.lat') { nil }
transform('city.coord.lon') { nil }
end

# OpenWeatherMap reports most of logical errors with HTTP code
# 200 and responses like {cod: "500", message: "Error message"}
post_process { |h|
transform { |h|
!h.key?('cod') || (200..400).cover?(h['cod'].to_i) or
fail "#{h['cod']}: #{h['message']}"
}

WEATHER_POST_PROCESSOR = lambda do |*|
WEATHER_PROCESSOR = lambda do |*|
# Most of the time there is exactly one weather item...
# ...but sometimes there are two. So, flatterning them looks
# more reasonable than having DataTable of 1-2 rows.
post_process { |h|
transform { |h|
h['weather2'] = h['weather'].last if h['weather'] && h['weather'].count > 1
}
post_process('weather', &:first)
transform('weather', &:first)

post_process('dt', &Time.method(:at))
post_process('dt_txt') { nil } # TODO: we need cleaner way to say "remove this"
post_process('sys.sunrise', &Time.method(:at))
post_process('sys.sunset', &Time.method(:at))
transform('dt', &Time.method(:at))
transform('dt_txt') { nil } # TODO: we need cleaner way to say "remove this"
transform('sys.sunrise', &Time.method(:at))
transform('sys.sunset', &Time.method(:at))

# https://github.com/zverok/geo_coord promo here!
post_process { |e|
transform { |e|
e['coord'] = Geo::Coord.new(e['coord.lat'], e['coord.lon']) if e['coord.lat'] && e['coord.lon']
}
post_process('coord.lat') { nil }
post_process('coord.lon') { nil }
transform('coord.lat') { nil }
transform('coord.lon') { nil }

# See http://openweathermap.org/weather-conditions#How-to-get-icon-URL
post_process('weather.icon') { |i| "http://openweathermap.org/img/w/#{i}.png" }
transform('weather.icon') { |i| "http://openweathermap.org/img/w/#{i}.png" }
end

# For endpoints returning weather in one place
instance_eval(&WEATHER_POST_PROCESSOR)
instance_eval(&WEATHER_PROCESSOR)

# For endpoints returning list of weathers (forecast or several
# cities).
post_process_items('list', &WEATHER_POST_PROCESSOR)
transform_items('list', &WEATHER_PROCESSOR)
end
end
end
Expand Down
8 changes: 4 additions & 4 deletions examples/tmdb_demo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class TMDB < TLAW::API
endpoint :movie do
param :query, required: true, keyword: false

post_process_items('results') {
post_process 'release_date', &Date.method(:parse)
transform_items('results') {
transform 'release_date', &Date.method(:parse)
}
end
end
Expand Down Expand Up @@ -88,8 +88,8 @@ class TMDB < TLAW::API
namespace :movies do
namespace :[] do
endpoint :images do
post_process_items 'posters' do
post_process('file_path') { |p| 'https://image.tmdb.org/t/p/original' + p }
transform_items 'posters' do
transform('file_path') { |p| 'https://image.tmdb.org/t/p/original' + p }
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/tlaw.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require 'backports/2.5.0/kernel/yield_self'
require 'open-uri'
require 'json'
require 'addressable/uri'
require 'addressable/template'

Expand Down Expand Up @@ -51,6 +50,6 @@ module TLAW
require_relative 'tlaw/namespace'
require_relative 'tlaw/api'

require_relative 'tlaw/response_processor'
require_relative 'tlaw/processors'

require_relative 'tlaw/dsl'
3 changes: 3 additions & 0 deletions lib/tlaw/api.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require_relative 'namespace'
require_relative 'dsl/api_wrapper'

module TLAW
# API is just a top-level {Namespace}.
#
Expand Down
14 changes: 9 additions & 5 deletions lib/tlaw/api_path.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
require_relative 'params/set'
require_relative 'processors/data_table_response_processor'
require 'forwardable'

module TLAW
# Base class for all API pathes: entire API, namespaces and endpoints.
# Base class for all API paths: entire API, namespaces and endpoints.
# Allows to define params and post-processors on any level.
#
class APIPath
class << self
# @private
attr_accessor :base_url, :path, :xml, :docs_link
attr_accessor :base_url, :path, :docs_link
attr_writer :response_processor

# @private
def symbol
Expand Down Expand Up @@ -40,14 +42,16 @@ def description
return unless @description || @docs_link

Util::Description.new(
[@description, ("Docs: #{@docs_link}" if @docs_link)]
.compact.join("\n\n")
[@description, @docs_link && "Docs: #{@docs_link}"].compact.join("\n\n")
)
end

# @private
def inherit(namespace, **attrs)
Class.new(self).tap do |subclass|
# Inherit response processor
attrs[:response_processor] ||= namespace.response_processor.class.new

attrs.each { |a, v| subclass.send("#{a}=", v) }
namespace.const_set(subclass.class_name, subclass)
end
Expand Down Expand Up @@ -79,7 +83,7 @@ def param_set

# @private
def response_processor
@response_processor ||= ResponseProcessor.new
@response_processor ||= Processors::DataTableResponseProcessor.new
end

# @private
Expand Down
Loading