apipie-rails 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/apipie/client/base.rb +98 -96
- data/lib/apipie/client/cli_command.rb +97 -95
- data/lib/apipie/client/main.rb +76 -74
- data/lib/apipie/client/thor.rb +14 -12
- data/lib/apipie/version.rb +1 -1
- metadata +4 -4
data/lib/apipie/client/base.rb
CHANGED
@@ -3,129 +3,131 @@ require 'oauth'
|
|
3
3
|
require 'json'
|
4
4
|
require 'apipie/client/rest_client_oauth'
|
5
5
|
|
6
|
-
module Apipie
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
module Apipie
|
7
|
+
module Client
|
8
|
+
|
9
|
+
class Base
|
10
|
+
attr_reader :client, :config
|
11
|
+
|
12
|
+
def initialize(config, options = { })
|
13
|
+
@client = RestClient::Resource.new config[:base_url],
|
14
|
+
:user => config[:username],
|
15
|
+
:password => config[:password],
|
16
|
+
:oauth => config[:oauth],
|
17
|
+
:headers => { :content_type => 'application/json',
|
18
|
+
:accept => 'application/json' }
|
19
|
+
@config = config
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def call(method, path, params = { }, headers = { })
|
23
|
+
headers ||= { }
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
args = [method]
|
26
|
+
if [:post, :put].include?(method)
|
27
|
+
args << params.to_json
|
28
|
+
else
|
29
|
+
headers[:params] = params if params
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
args << headers if headers
|
33
|
+
process_data client[path].send(*args)
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def self.doc
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
def self.validation_hash(method)
|
41
|
+
validation_hashes[method.to_s]
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
def self.method_doc(method)
|
45
|
+
method_docs[method.to_s]
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
def validate_params!(params, rules)
|
49
|
+
return unless params.is_a?(Hash)
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
invalid_keys = params.keys.map(&:to_s) - (rules.is_a?(Hash) ? rules.keys : rules)
|
52
|
+
raise ArgumentError, "Invalid keys: #{invalid_keys.join(", ")}" unless invalid_keys.empty?
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
if rules.is_a? Hash
|
55
|
+
rules.each do |key, sub_keys|
|
56
|
+
validate_params!(params[key], sub_keys) if params[key]
|
57
|
+
end
|
56
58
|
end
|
57
59
|
end
|
58
|
-
end
|
59
60
|
|
60
|
-
|
61
|
+
protected
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
def process_data(response)
|
64
|
+
data = begin
|
65
|
+
JSON.parse(response.body)
|
66
|
+
rescue JSON::ParserError
|
67
|
+
response.body
|
68
|
+
end
|
69
|
+
return data, response
|
67
70
|
end
|
68
|
-
return data, response
|
69
|
-
end
|
70
|
-
|
71
|
-
def check_params(params, options = { })
|
72
|
-
raise ArgumentError unless (method = options[:method])
|
73
|
-
return unless config[:enable_validations]
|
74
71
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
72
|
+
def check_params(params, options = { })
|
73
|
+
raise ArgumentError unless (method = options[:method])
|
74
|
+
return unless config[:enable_validations]
|
75
|
+
|
76
|
+
case options[:allowed]
|
77
|
+
when true
|
78
|
+
validate_params!(params, self.class.validation_hash(method))
|
79
|
+
when false
|
80
|
+
raise ArgumentError, "this method '#{method}' does not support params" if params && !params.empty?
|
81
|
+
else
|
82
|
+
raise ArgumentError, "options :allowed should be true or false, it was #{options[:allowed]}"
|
83
|
+
end
|
82
84
|
end
|
83
|
-
end
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
86
|
+
# @return url and rest of the params
|
87
|
+
def fill_params_in_url(url, params)
|
88
|
+
params ||= { }
|
89
|
+
# insert param values
|
90
|
+
url_param_names = params_in_path(url)
|
91
|
+
url = params_in_path(url).inject(url) do |url, param_name|
|
92
|
+
param_value = params[param_name] or
|
93
|
+
raise ArgumentError, "missing param '#{param_name}' in parameters"
|
94
|
+
url.sub(":#{param_name}", param_value.to_s)
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
-
|
97
|
+
return url, params.reject { |param_name, _| url_param_names.include? param_name }
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
+
private
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
def self.method_docs
|
103
|
+
@method_docs ||= doc['methods'].inject({ }) do |hash, method|
|
104
|
+
hash[method['name']] = method
|
105
|
+
hash
|
106
|
+
end
|
105
107
|
end
|
106
|
-
end
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
109
|
+
def self.validation_hashes
|
110
|
+
@validation_hashes ||= method_docs.inject({ }) do |hash, pair|
|
111
|
+
name, method_doc = pair
|
112
|
+
hash[name] = construct_validation_hash method_doc
|
113
|
+
hash
|
114
|
+
end
|
113
115
|
end
|
114
|
-
end
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
def self.construct_validation_hash(method)
|
118
|
+
if method['params'].any? { |p| p['params'] }
|
119
|
+
method['params'].reduce({ }) do |h, p|
|
120
|
+
h.update(p['name'] => (p['params'] ? p['params'].map { |pp| pp['name'] } : nil))
|
121
|
+
end
|
122
|
+
else
|
123
|
+
method['params'].map { |p| p['name'] }
|
120
124
|
end
|
121
|
-
else
|
122
|
-
method['params'].map { |p| p['name'] }
|
123
125
|
end
|
124
|
-
end
|
125
126
|
|
126
|
-
|
127
|
-
|
128
|
-
|
127
|
+
def params_in_path(url)
|
128
|
+
url.scan(/:([^\/]*)/).map { |m| m.first }
|
129
|
+
end
|
129
130
|
|
131
|
+
end
|
130
132
|
end
|
131
133
|
end
|
@@ -1,127 +1,129 @@
|
|
1
|
-
module Apipie
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Apipie
|
2
|
+
module Client
|
3
|
+
class CliCommand < Thor
|
4
|
+
no_tasks do
|
5
|
+
def client
|
6
|
+
resource_class = apipie_options[:client_module]::Resources.const_get(self.class.name[/[^:]*$/])
|
7
|
+
@client ||= resource_class.new(apipie_options[:config])
|
8
|
+
end
|
9
|
+
|
10
|
+
def transform_options(inline_params, transform_hash = { })
|
11
|
+
# we use not mentioned params without change
|
12
|
+
transformed_options = (options.keys - transform_hash.values.flatten - inline_params).reduce({ }) { |h, k| h.update(k => options[k]) }
|
8
13
|
|
9
|
-
|
10
|
-
# we use not mentioned params without change
|
11
|
-
transformed_options = (options.keys - transform_hash.values.flatten - inline_params).reduce({ }) { |h, k| h.update(k => options[k]) }
|
14
|
+
inline_params.each { |p| transformed_options[p] = options[p] }
|
12
15
|
|
13
|
-
|
16
|
+
transform_hash.each do |sub_key, params|
|
17
|
+
transformed_options[sub_key] = { }
|
18
|
+
params.each { |p| transformed_options[sub_key][p] = options[p] if options.has_key?(p) }
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
transformed_options[sub_key] = { }
|
17
|
-
params.each { |p| transformed_options[sub_key][p] = options[p] if options.has_key?(p) }
|
21
|
+
return transformed_options
|
18
22
|
end
|
19
23
|
|
20
|
-
|
21
|
-
|
24
|
+
def print_data(data)
|
25
|
+
case data
|
26
|
+
when Array
|
27
|
+
print_big_table(table_from_array(data))
|
28
|
+
when Hash
|
29
|
+
print_table(table_from_hash(data))
|
30
|
+
else
|
31
|
+
print_unknown(data)
|
32
|
+
end
|
33
|
+
end
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
35
|
+
# unifies the data for further processing. e.g.
|
36
|
+
#
|
37
|
+
# { "user" => {"username" => "test", "password" => "changeme" }
|
38
|
+
#
|
39
|
+
# becomes:
|
40
|
+
#
|
41
|
+
# { "username" => "test", "password" => "changeme" }
|
42
|
+
def normalize_item_data(item)
|
43
|
+
if item.size == 1 && item.values.first.is_a?(Hash)
|
44
|
+
item.values.first
|
29
45
|
else
|
30
|
-
|
46
|
+
item
|
47
|
+
end
|
31
48
|
end
|
32
|
-
end
|
33
49
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
item
|
50
|
+
def table_from_array(data)
|
51
|
+
return [] if data.empty?
|
52
|
+
table = []
|
53
|
+
items = data.map { |item| normalize_item_data(item) }
|
54
|
+
columns = items.first.keys
|
55
|
+
table << columns
|
56
|
+
items.each do |item|
|
57
|
+
row = columns.map { |c| item[c] }
|
58
|
+
table << row.map(&:to_s)
|
59
|
+
end
|
60
|
+
return table
|
46
61
|
end
|
47
|
-
end
|
48
62
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
row = columns.map { |c| item[c] }
|
57
|
-
table << row.map(&:to_s)
|
63
|
+
def table_from_hash(data)
|
64
|
+
return [] if data.empty?
|
65
|
+
table = []
|
66
|
+
normalize_item_data(data).each do |k, v|
|
67
|
+
table << ["#{k}:", v].map(&:to_s)
|
68
|
+
end
|
69
|
+
table
|
58
70
|
end
|
59
|
-
return table
|
60
|
-
end
|
61
71
|
|
62
|
-
|
63
|
-
|
64
|
-
table = []
|
65
|
-
normalize_item_data(data).each do |k, v|
|
66
|
-
table << ["#{k}:", v].map(&:to_s)
|
72
|
+
def print_unknown(data)
|
73
|
+
say data
|
67
74
|
end
|
68
|
-
table
|
69
|
-
end
|
70
75
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
76
|
+
def print_big_table(table, options={ })
|
77
|
+
return if table.empty?
|
74
78
|
|
75
|
-
|
76
|
-
|
79
|
+
formats, ident, colwidth = [], options[:ident].to_i, options[:colwidth]
|
80
|
+
options[:truncate] = terminal_width if options[:truncate] == true
|
77
81
|
|
78
|
-
|
79
|
-
|
82
|
+
formats << "%-#{colwidth + 2}s" if colwidth
|
83
|
+
start = colwidth ? 1 : 0
|
80
84
|
|
81
|
-
|
82
|
-
|
85
|
+
start.upto(table.first.length - 2) do |i|
|
86
|
+
maxima ||= table.max { |a, b| a[i].size <=> b[i].size }[i].size
|
87
|
+
formats << "%-#{maxima + 2}s"
|
88
|
+
end
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
formats << "%-#{maxima + 2}s"
|
87
|
-
end
|
90
|
+
formats << "%s"
|
91
|
+
formats[0] = formats[0].insert(0, " " * ident)
|
88
92
|
|
89
|
-
|
90
|
-
|
93
|
+
header_printed = false
|
94
|
+
table.each do |row|
|
95
|
+
sentence = ""
|
91
96
|
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
row.each_with_index do |column, i|
|
98
|
+
sentence << formats[i] % column.to_s
|
99
|
+
end
|
95
100
|
|
96
|
-
|
97
|
-
sentence
|
101
|
+
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
|
102
|
+
$stdout.puts sentence
|
103
|
+
say(set_color("-" * sentence.size, :green)) unless header_printed
|
104
|
+
header_printed = true
|
98
105
|
end
|
99
|
-
|
100
|
-
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
|
101
|
-
$stdout.puts sentence
|
102
|
-
say(set_color("-" * sentence.size, :green)) unless header_printed
|
103
|
-
header_printed = true
|
104
106
|
end
|
107
|
+
|
105
108
|
end
|
106
109
|
|
107
|
-
|
110
|
+
class << self
|
111
|
+
def help(shell, subcommand = true)
|
112
|
+
list = self.printable_tasks(true, subcommand)
|
113
|
+
Thor::Util.thor_classes_in(self).each do |klass|
|
114
|
+
list += printable_tasks(false)
|
115
|
+
end
|
116
|
+
list.sort! { |a, b| a[0] <=> b[0] }
|
108
117
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
list += printable_tasks(false)
|
118
|
+
shell.say
|
119
|
+
shell.print_table(list, :indent => 2, :truncate => true)
|
120
|
+
shell.say
|
121
|
+
Thor.send(:class_options_help, shell)
|
114
122
|
end
|
115
|
-
list.sort! { |a, b| a[0] <=> b[0] }
|
116
123
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
Thor.send(:class_options_help, shell)
|
121
|
-
end
|
122
|
-
|
123
|
-
def banner(task, namespace = nil, subcommand = false)
|
124
|
-
task.name
|
124
|
+
def banner(task, namespace = nil, subcommand = false)
|
125
|
+
task.name
|
126
|
+
end
|
125
127
|
end
|
126
128
|
end
|
127
129
|
end
|
data/lib/apipie/client/main.rb
CHANGED
@@ -1,97 +1,99 @@
|
|
1
1
|
require "apipie/client/thor"
|
2
2
|
require "apipie/client/cli_command"
|
3
3
|
|
4
|
-
module Apipie
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
4
|
+
module Apipie
|
5
|
+
module Client
|
6
|
+
class Main < Thor
|
7
|
+
|
8
|
+
def help(meth = nil)
|
9
|
+
if meth && !self.respond_to?(meth)
|
10
|
+
initialize_thorfiles(meth)
|
11
|
+
klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
|
12
|
+
self.class.handle_no_task_error(task, false) if klass.nil?
|
13
|
+
klass.start(["-h", task].compact, :shell => self.shell)
|
14
|
+
else
|
15
|
+
say "#{apipie_options[:name].capitalize} CLI"
|
16
|
+
say
|
17
|
+
invoke :commands
|
18
|
+
end
|
17
19
|
end
|
18
|
-
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
desc "commands [SEARCH]", "List the available commands"
|
22
|
+
def commands(search="")
|
23
|
+
initialize_thorfiles
|
24
|
+
klasses = Thor::Base.subclasses
|
25
|
+
display_klasses(false, false, klasses)
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
class << self
|
29
|
+
private
|
30
|
+
def dispatch(task, given_args, given_options, config)
|
31
|
+
parser = Thor::Options.new :auth => Thor::Option.parse(%w[auth -a], :string)
|
32
|
+
opts = parser.parse(given_args)
|
33
|
+
if opts['auth']
|
34
|
+
username, password = opts['auth'].split(':')
|
35
|
+
apipie_options[:config][:username] = username
|
36
|
+
apipie_options[:config][:password] = password
|
37
|
+
end
|
38
|
+
remaining = parser.remaining
|
39
|
+
|
40
|
+
super(task, remaining, given_options, config)
|
36
41
|
end
|
37
|
-
remaining = parser.remaining
|
38
|
-
|
39
|
-
super(task, remaining, given_options, config)
|
40
42
|
end
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
+
private
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
def method_missing(meth, *args)
|
47
|
+
meth = meth.to_s
|
48
|
+
initialize_thorfiles(meth)
|
49
|
+
klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
|
50
|
+
args.unshift(task) if task
|
51
|
+
klass.start(args, :shell => self.shell)
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
# Load the thorfiles. If relevant_to is supplied, looks for specific files
|
55
|
+
# in the thor_root instead of loading them all.
|
56
|
+
#
|
57
|
+
# By default, it also traverses the current path until find Thor files, as
|
58
|
+
# described in thorfiles. This look up can be skipped by suppliying
|
59
|
+
# skip_lookup true.
|
60
|
+
#
|
61
|
+
def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
|
62
|
+
thorfiles.each do |f|
|
63
|
+
Thor::Util.load_thorfile(f, nil, options[:debug])
|
64
|
+
end
|
63
65
|
end
|
64
|
-
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
def thorfiles
|
68
|
+
Dir[File.expand_path("*/commands/*.thor", apipie_options[:root])]
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
# Display information about the given klasses. If with_module is given,
|
72
|
+
# it shows a table with information extracted from the yaml file.
|
73
|
+
#
|
74
|
+
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
75
|
+
klasses -= [Thor, Main, ::Apipie::Client::CliCommand, ::Thor] unless show_internal
|
75
76
|
|
76
|
-
|
77
|
+
show_modules if with_modules && !thor_yaml.empty?
|
77
78
|
|
78
|
-
|
79
|
-
|
79
|
+
list = Hash.new { |h, k| h[k] = [] }
|
80
|
+
groups = []
|
80
81
|
|
81
|
-
|
82
|
-
|
82
|
+
# Get classes which inherit from Thor
|
83
|
+
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_tasks(false) }
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
# Get classes which inherit from Thor::Base
|
86
|
+
groups.map! { |k| k.printable_tasks(false).first }
|
87
|
+
list["root"] = groups
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
# Order namespaces with default coming first
|
90
|
+
list = list.sort { |a, b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
|
91
|
+
list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
-
|
94
|
+
def display_tasks(namespace, list) #:nodoc:
|
95
|
+
say namespace
|
96
|
+
end
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
data/lib/apipie/client/thor.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
-
module Apipie
|
2
|
-
|
1
|
+
module Apipie
|
2
|
+
module Client
|
3
|
+
class Thor < ::Thor
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
def self.apipie_options
|
6
|
+
Apipie::Client::Thor.instance_variable_get :@apipie_options
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
no_tasks do
|
10
|
+
def apipie_options
|
11
|
+
self.class.apipie_options
|
12
|
+
end
|
11
13
|
end
|
12
|
-
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def self.apipie_options=(options)
|
16
|
+
Apipie::Client::Thor.instance_variable_set :@apipie_options, options
|
17
|
+
end
|
17
18
|
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
data/lib/apipie/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apipie-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 13
|
10
|
+
version: 0.0.13
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Pavel Pokorny
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2012-10-
|
19
|
+
date: 2012-10-25 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: rspec-rails
|