sawyer 0.0.4 → 0.0.7
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.
- data/Rakefile +0 -123
- data/lib/sawyer.rb +2 -1
- data/lib/sawyer/agent.rb +16 -2
- data/lib/sawyer/hal_rels_parser.rb +13 -0
- data/lib/sawyer/relation.rb +14 -4
- data/lib/sawyer/resource.rb +58 -10
- data/lib/sawyer/response.rb +1 -2
- data/lib/sawyer/serializer.rb +14 -3
- data/sawyer.gemspec +24 -69
- data/script/console +8 -0
- data/script/package +8 -0
- data/script/release +18 -0
- data/script/test +5 -0
- data/test/agent_test.rb +55 -0
- data/test/relation_test.rb +52 -0
- data/test/resource_test.rb +36 -13
- data/test/response_test.rb +10 -0
- metadata +17 -22
- data/SPEC.md +0 -71
- data/example/client.rb +0 -50
- data/example/nigiri.schema.json +0 -47
- data/example/server.rb +0 -114
- data/example/user.schema.json +0 -51
data/Rakefile
CHANGED
@@ -1,47 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require 'date'
|
4
|
-
|
5
|
-
#############################################################################
|
6
|
-
#
|
7
|
-
# Helper functions
|
8
|
-
#
|
9
|
-
#############################################################################
|
10
|
-
|
11
|
-
def name
|
12
|
-
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
-
end
|
14
|
-
|
15
|
-
def version
|
16
|
-
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
-
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
-
end
|
19
|
-
|
20
|
-
def date
|
21
|
-
Date.today.to_s
|
22
|
-
end
|
23
|
-
|
24
|
-
def rubyforge_project
|
25
|
-
name
|
26
|
-
end
|
27
|
-
|
28
|
-
def gemspec_file
|
29
|
-
"#{name}.gemspec"
|
30
|
-
end
|
31
|
-
|
32
|
-
def gem_file
|
33
|
-
"#{name}-#{version}.gem"
|
34
|
-
end
|
35
|
-
|
36
|
-
def replace_header(head, header_name)
|
37
|
-
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
-
end
|
39
|
-
|
40
|
-
#############################################################################
|
41
|
-
#
|
42
|
-
# Standard tasks
|
43
|
-
#
|
44
|
-
#############################################################################
|
45
3
|
|
46
4
|
task :default => :test
|
47
5
|
|
@@ -52,84 +10,3 @@ Rake::TestTask.new(:test) do |test|
|
|
52
10
|
test.verbose = true
|
53
11
|
end
|
54
12
|
|
55
|
-
desc "Open an irb session preloaded with this library"
|
56
|
-
task :console do
|
57
|
-
sh "irb -rubygems -r ./lib/#{name}.rb"
|
58
|
-
end
|
59
|
-
|
60
|
-
#############################################################################
|
61
|
-
#
|
62
|
-
# Custom tasks (add your own tasks here)
|
63
|
-
#
|
64
|
-
#############################################################################
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
#############################################################################
|
69
|
-
#
|
70
|
-
# Packaging tasks
|
71
|
-
#
|
72
|
-
#############################################################################
|
73
|
-
|
74
|
-
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
75
|
-
task :release => :build do
|
76
|
-
unless `git branch` =~ /^\* master$/
|
77
|
-
puts "You must be on the master branch to release!"
|
78
|
-
exit!
|
79
|
-
end
|
80
|
-
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
81
|
-
sh "git tag v#{version}"
|
82
|
-
sh "git push origin master"
|
83
|
-
sh "git push origin v#{version}"
|
84
|
-
sh "gem push pkg/#{name}-#{version}.gem"
|
85
|
-
end
|
86
|
-
|
87
|
-
desc "Build #{gem_file} into the pkg directory"
|
88
|
-
task :build => :gemspec do
|
89
|
-
sh "mkdir -p pkg"
|
90
|
-
sh "gem build #{gemspec_file}"
|
91
|
-
sh "mv #{gem_file} pkg"
|
92
|
-
end
|
93
|
-
|
94
|
-
desc "Generate #{gemspec_file}"
|
95
|
-
task :gemspec => :validate do
|
96
|
-
# read spec file and split out manifest section
|
97
|
-
spec = File.read(gemspec_file)
|
98
|
-
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
99
|
-
|
100
|
-
# replace name version and date
|
101
|
-
replace_header(head, :name)
|
102
|
-
replace_header(head, :version)
|
103
|
-
replace_header(head, :date)
|
104
|
-
#comment this out if your rubyforge_project has a different name
|
105
|
-
replace_header(head, :rubyforge_project)
|
106
|
-
|
107
|
-
# determine file list from git ls-files
|
108
|
-
files = `git ls-files`.
|
109
|
-
split("\n").
|
110
|
-
sort.
|
111
|
-
reject { |file| file =~ /^\./ }.
|
112
|
-
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
113
|
-
map { |file| " #{file}" }.
|
114
|
-
join("\n")
|
115
|
-
|
116
|
-
# piece file back together and write
|
117
|
-
manifest = " s.files = %w[\n#{files}\n ]\n"
|
118
|
-
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
119
|
-
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
120
|
-
puts "Updated #{gemspec_file}"
|
121
|
-
end
|
122
|
-
|
123
|
-
desc "Validate #{gemspec_file}"
|
124
|
-
task :validate do
|
125
|
-
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
126
|
-
unless libfiles.empty?
|
127
|
-
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
128
|
-
exit!
|
129
|
-
end
|
130
|
-
unless Dir['VERSION*'].empty?
|
131
|
-
puts "A `VERSION` file at root level violates Gem best practices."
|
132
|
-
exit!
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
data/lib/sawyer.rb
CHANGED
data/lib/sawyer/agent.rb
CHANGED
@@ -5,6 +5,9 @@ module Sawyer
|
|
5
5
|
class Agent
|
6
6
|
NO_BODY = Set.new([:get, :head])
|
7
7
|
|
8
|
+
attr_accessor :links_parser
|
9
|
+
attr_accessor :allow_undefined_methods
|
10
|
+
|
8
11
|
class << self
|
9
12
|
attr_writer :serializer
|
10
13
|
end
|
@@ -33,8 +36,11 @@ module Sawyer
|
|
33
36
|
# Yields the Faraday::Connection if a block is given.
|
34
37
|
def initialize(endpoint, options = nil)
|
35
38
|
@endpoint = endpoint
|
36
|
-
@conn = (options && options[:faraday]) || Faraday.new
|
39
|
+
@conn = (options && options[:faraday]) || Faraday.new
|
37
40
|
@serializer = (options && options[:serializer]) || self.class.serializer
|
41
|
+
@links_parser = (options && options[:links_parser]) || HalLinksParser.new
|
42
|
+
@allow_undefined_methods = (options && options[:allow_undefined_methods])
|
43
|
+
@conn.url_prefix = @endpoint
|
38
44
|
yield @conn if block_given?
|
39
45
|
end
|
40
46
|
|
@@ -42,7 +48,7 @@ module Sawyer
|
|
42
48
|
#
|
43
49
|
# Returns a Sawyer::Relation::Map.
|
44
50
|
def rels
|
45
|
-
@rels ||= root.data.
|
51
|
+
@rels ||= root.data._rels
|
46
52
|
end
|
47
53
|
|
48
54
|
# Public: Retains a reference to the root response of the API.
|
@@ -115,12 +121,20 @@ module Sawyer
|
|
115
121
|
@serializer.decode(str)
|
116
122
|
end
|
117
123
|
|
124
|
+
def parse_links(data)
|
125
|
+
@links_parser.parse(data)
|
126
|
+
end
|
127
|
+
|
118
128
|
def expand_url(url, options = nil)
|
119
129
|
tpl = url.respond_to?(:expand) ? url : URITemplate.new(url.to_s)
|
120
130
|
expand = tpl.method(:expand)
|
121
131
|
options ? expand.call(options) : expand.call
|
122
132
|
end
|
123
133
|
|
134
|
+
def allow_undefined_methods?
|
135
|
+
!!@allow_undefined_methods
|
136
|
+
end
|
137
|
+
|
124
138
|
def inspect
|
125
139
|
%(<#{self.class} #{@endpoint}>)
|
126
140
|
end
|
data/lib/sawyer/relation.rb
CHANGED
@@ -13,7 +13,7 @@ module Sawyer
|
|
13
13
|
#
|
14
14
|
# Returns nothing.
|
15
15
|
def <<(rel)
|
16
|
-
@map[rel.name] = rel
|
16
|
+
@map[rel.name] = rel if rel
|
17
17
|
end
|
18
18
|
|
19
19
|
# Gets the raw Relation by its name.
|
@@ -82,7 +82,12 @@ module Sawyer
|
|
82
82
|
#
|
83
83
|
# Returns a Relation.
|
84
84
|
def self.from_link(agent, name, options)
|
85
|
-
|
85
|
+
case options
|
86
|
+
when Hash
|
87
|
+
new agent, name, options[:href], options[:method]
|
88
|
+
when String
|
89
|
+
new agent, name, options
|
90
|
+
end
|
86
91
|
end
|
87
92
|
|
88
93
|
# A Relation represents an available next action for a resource.
|
@@ -94,7 +99,11 @@ module Sawyer
|
|
94
99
|
def initialize(agent, name, href, method = nil)
|
95
100
|
@agent = agent
|
96
101
|
@name = name.to_sym
|
97
|
-
@
|
102
|
+
@href = href
|
103
|
+
begin
|
104
|
+
@href_template = URITemplate.new(href.to_s)
|
105
|
+
rescue URITemplate::RFC6570::Invalid => e
|
106
|
+
end
|
98
107
|
|
99
108
|
methods = nil
|
100
109
|
|
@@ -225,6 +234,7 @@ module Sawyer
|
|
225
234
|
end
|
226
235
|
|
227
236
|
def href(options = nil)
|
237
|
+
return @href if @href_template.nil?
|
228
238
|
method = @href_template.method(:expand)
|
229
239
|
options ? method.call(options) : method.call
|
230
240
|
end
|
@@ -243,7 +253,7 @@ module Sawyer
|
|
243
253
|
# Returns a Sawyer::Response.
|
244
254
|
def call(data = nil, options = nil)
|
245
255
|
m = options && options[:method]
|
246
|
-
if m && !@available_methods.include?(m == :head ? :get : m)
|
256
|
+
if m && !@agent.allow_undefined_methods? && !@available_methods.include?(m == :head ? :get : m)
|
247
257
|
raise ArgumentError, "method #{m.inspect} is not available: #{@available_methods.to_a.inspect}"
|
248
258
|
end
|
249
259
|
|
data/lib/sawyer/resource.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
module Sawyer
|
2
2
|
class Resource
|
3
|
-
SPECIAL_METHODS = Set.new
|
3
|
+
SPECIAL_METHODS = Set.new(%w(agent rels fields))
|
4
4
|
attr_reader :_agent, :_rels, :_fields
|
5
|
+
attr_reader :attrs
|
6
|
+
alias to_hash attrs
|
5
7
|
|
6
8
|
# Initializes a Resource with the given data.
|
7
9
|
#
|
8
10
|
# agent - The Sawyer::Agent that made the API request.
|
9
11
|
# data - Hash of key/value properties.
|
10
|
-
def initialize(agent, data)
|
12
|
+
def initialize(agent, data = {})
|
11
13
|
@_agent = agent
|
12
|
-
|
13
|
-
@
|
14
|
+
data, links = agent.parse_links(data)
|
15
|
+
@_rels = Relation.from_links(agent, links)
|
16
|
+
@_fields = Set.new
|
17
|
+
@_metaclass = (class << self; self; end)
|
18
|
+
@attrs = {}
|
14
19
|
data.each do |key, value|
|
15
20
|
@_fields << key
|
16
|
-
|
21
|
+
@attrs[key.to_sym] = process_value(value)
|
17
22
|
end
|
23
|
+
@_metaclass.send(:attr_accessor, *data.keys)
|
18
24
|
end
|
19
25
|
|
20
26
|
# Processes an individual value of this resource. Hashes get exploded
|
@@ -40,6 +46,29 @@ module Sawyer
|
|
40
46
|
@_fields.include? key
|
41
47
|
end
|
42
48
|
|
49
|
+
# Allow fields to be retrieved via Hash notation
|
50
|
+
#
|
51
|
+
# method - key name
|
52
|
+
#
|
53
|
+
# Returns the value from attrs if exists
|
54
|
+
def [](method)
|
55
|
+
send(method.to_sym)
|
56
|
+
rescue NoMethodError
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Allow fields to be set via Hash notation
|
61
|
+
#
|
62
|
+
# method - key name
|
63
|
+
# value - value to set for the attr key
|
64
|
+
#
|
65
|
+
# Returns - value
|
66
|
+
def []=(method, value)
|
67
|
+
send("#{method}=", value)
|
68
|
+
rescue NoMethodError
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
43
72
|
ATTR_SETTER = '='.freeze
|
44
73
|
ATTR_PREDICATE = '?'.freeze
|
45
74
|
|
@@ -47,14 +76,14 @@ module Sawyer
|
|
47
76
|
def method_missing(method, *args)
|
48
77
|
attr_name, suffix = method.to_s.scan(/([a-z0-9\_]+)(\?|\=)?$/i).first
|
49
78
|
if suffix == ATTR_SETTER
|
50
|
-
|
79
|
+
@_metaclass.send(:attr_accessor, attr_name)
|
51
80
|
@_fields << attr_name.to_sym
|
52
|
-
|
53
|
-
elsif @_fields.include?(attr_name.to_sym)
|
54
|
-
value =
|
81
|
+
send(method, args.first)
|
82
|
+
elsif attr_name && @_fields.include?(attr_name.to_sym)
|
83
|
+
value = @attrs[attr_name.to_sym]
|
55
84
|
case suffix
|
56
85
|
when nil
|
57
|
-
|
86
|
+
@_metaclass.send(:attr_accessor, attr_name)
|
58
87
|
value
|
59
88
|
when ATTR_PREDICATE then !!value
|
60
89
|
end
|
@@ -64,6 +93,25 @@ module Sawyer
|
|
64
93
|
super
|
65
94
|
end
|
66
95
|
end
|
96
|
+
|
97
|
+
# Wire up accessor methods to pull from attrs
|
98
|
+
def self.attr_accessor(*attrs)
|
99
|
+
attrs.each do |attribute|
|
100
|
+
class_eval do
|
101
|
+
define_method attribute do
|
102
|
+
@attrs[attribute.to_sym]
|
103
|
+
end
|
104
|
+
|
105
|
+
define_method "#{attribute}=" do |value|
|
106
|
+
@attrs[attribute.to_sym] = value
|
107
|
+
end
|
108
|
+
|
109
|
+
define_method "#{attribute}?" do
|
110
|
+
!!@attrs[attribute.to_sym]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
67
115
|
end
|
68
116
|
end
|
69
117
|
|
data/lib/sawyer/response.rb
CHANGED
data/lib/sawyer/serializer.rb
CHANGED
@@ -49,6 +49,8 @@ module Sawyer
|
|
49
49
|
@dump.call(encode_object(data))
|
50
50
|
end
|
51
51
|
|
52
|
+
alias dump encode
|
53
|
+
|
52
54
|
# Public: Decodes a String into an Object (usually a Hash or Array of
|
53
55
|
# Hashes).
|
54
56
|
#
|
@@ -60,10 +62,12 @@ module Sawyer
|
|
60
62
|
decode_object(@load.call(data))
|
61
63
|
end
|
62
64
|
|
65
|
+
alias load decode
|
66
|
+
|
63
67
|
def encode_object(data)
|
64
68
|
case data
|
65
69
|
when Hash then encode_hash(data)
|
66
|
-
when Array then data.map { |o| encode_object(
|
70
|
+
when Array then data.map { |o| encode_object(o) }
|
67
71
|
else data
|
68
72
|
end
|
69
73
|
end
|
@@ -73,6 +77,7 @@ module Sawyer
|
|
73
77
|
case value = hash[key]
|
74
78
|
when Date then hash[key] = value.to_time.utc.xmlschema
|
75
79
|
when Time then hash[key] = value.utc.xmlschema
|
80
|
+
when Hash then hash[key] = encode_hash(value)
|
76
81
|
end
|
77
82
|
end
|
78
83
|
hash
|
@@ -81,7 +86,7 @@ module Sawyer
|
|
81
86
|
def decode_object(data)
|
82
87
|
case data
|
83
88
|
when Hash then decode_hash(data)
|
84
|
-
when Array then data.map { |o| decode_object(
|
89
|
+
when Array then data.map { |o| decode_object(o) }
|
85
90
|
else data
|
86
91
|
end
|
87
92
|
end
|
@@ -94,13 +99,19 @@ module Sawyer
|
|
94
99
|
end
|
95
100
|
|
96
101
|
def decode_hash_value(key, value)
|
97
|
-
if key
|
102
|
+
if time_field?(key, value)
|
98
103
|
Time.parse(value)
|
99
104
|
elsif value.is_a?(Hash)
|
100
105
|
decode_hash(value)
|
106
|
+
elsif value.is_a?(Array)
|
107
|
+
value.map { |o| decode_hash_value(key, o) }
|
101
108
|
else
|
102
109
|
value
|
103
110
|
end
|
104
111
|
end
|
112
|
+
|
113
|
+
def time_field?(key, value)
|
114
|
+
value && key =~ /_(at|on)$/
|
115
|
+
end
|
105
116
|
end
|
106
117
|
end
|
data/sawyer.gemspec
CHANGED
@@ -1,78 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
## You can find comprehensive Gem::Specification documentation, at
|
6
|
-
## http://docs.rubygems.org/read/chapter/20
|
7
|
-
Gem::Specification.new do |s|
|
8
|
-
s.specification_version = 2 if s.respond_to? :specification_version=
|
9
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
-
s.rubygems_version = '1.3.5'
|
1
|
+
lib = "sawyer"
|
2
|
+
lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__)
|
3
|
+
File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/
|
4
|
+
version = $1
|
11
5
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
s.name = 'sawyer'
|
16
|
-
s.version = '0.0.4'
|
17
|
-
s.date = '2012-09-27'
|
18
|
-
s.rubyforge_project = 'sawyer'
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.specification_version = 2 if spec.respond_to? :specification_version=
|
8
|
+
spec.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if spec.respond_to? :required_rubygems_version=
|
19
9
|
|
20
|
-
|
21
|
-
|
22
|
-
s.summary = "Secret User Agent of HTTP"
|
23
|
-
s.description = "#{s.summary} built on Faraday"
|
10
|
+
spec.name = lib
|
11
|
+
spec.version = version
|
24
12
|
|
25
|
-
|
26
|
-
## better to set the email to an email list or something. If you don't have
|
27
|
-
## a custom homepage, consider using your GitHub URL or the like.
|
28
|
-
s.authors = ["Rick Olson"]
|
29
|
-
s.email = 'technoweenie@gmail.com'
|
30
|
-
s.homepage = 'https://github.com/technoweenie/sawyer'
|
13
|
+
spec.summary = "Secret User Agent of HTTP"
|
31
14
|
|
32
|
-
|
33
|
-
|
34
|
-
|
15
|
+
spec.authors = ["Rick Olson"]
|
16
|
+
spec.email = 'technoweenie@gmail.com'
|
17
|
+
spec.homepage = 'https://github.com/lostisland/sawyer'
|
18
|
+
spec.licenses = ['MIT']
|
35
19
|
|
36
|
-
|
37
|
-
|
38
|
-
s.add_dependency('faraday', ['~> 0.8.4'])
|
39
|
-
s.add_dependency('uri_template', ['~> 0.5.0'])
|
20
|
+
spec.add_dependency 'faraday', ['~> 0.8.4']
|
21
|
+
spec.add_dependency 'uri_template', ['~> 0.5.0']
|
40
22
|
|
41
|
-
|
42
|
-
|
43
|
-
|
23
|
+
spec.files = %w(Gemfile LICENSE.md README.md Rakefile)
|
24
|
+
spec.files << "#{lib}.gemspec"
|
25
|
+
spec.files += Dir.glob("lib/**/*.rb")
|
26
|
+
spec.files += Dir.glob("test/**/*.rb")
|
27
|
+
spec.files += Dir.glob("script/*")
|
44
28
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# = MANIFEST =
|
49
|
-
s.files = %w[
|
50
|
-
Gemfile
|
51
|
-
LICENSE.md
|
52
|
-
README.md
|
53
|
-
Rakefile
|
54
|
-
SPEC.md
|
55
|
-
example/client.rb
|
56
|
-
example/nigiri.schema.json
|
57
|
-
example/server.rb
|
58
|
-
example/user.schema.json
|
59
|
-
lib/sawyer.rb
|
60
|
-
lib/sawyer/agent.rb
|
61
|
-
lib/sawyer/relation.rb
|
62
|
-
lib/sawyer/resource.rb
|
63
|
-
lib/sawyer/response.rb
|
64
|
-
lib/sawyer/serializer.rb
|
65
|
-
sawyer.gemspec
|
66
|
-
test/agent_test.rb
|
67
|
-
test/helper.rb
|
68
|
-
test/relation_test.rb
|
69
|
-
test/resource_test.rb
|
70
|
-
test/response_test.rb
|
71
|
-
]
|
72
|
-
# = MANIFEST =
|
73
|
-
|
74
|
-
## Test files will be grabbed from the file list. Make sure the path glob
|
75
|
-
## matches what you actually use.
|
76
|
-
s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
|
29
|
+
dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL'
|
30
|
+
git_files = `git ls-files -z 2>#{dev_null}`
|
31
|
+
spec.files &= git_files.split("\0") if $?.success?
|
77
32
|
end
|
78
33
|
|
data/script/console
ADDED
data/script/package
ADDED
data/script/release
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Usage: script/release
|
3
|
+
# Build the package, tag a commit, push it to origin, and then release the
|
4
|
+
# package publicly.
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
version="$(script/package | grep Version: | awk '{print $2}')"
|
9
|
+
[ -n "$version" ] || exit 1
|
10
|
+
|
11
|
+
git commit --allow-empty -a -m "Release $version"
|
12
|
+
git tag "v$version"
|
13
|
+
git push origin
|
14
|
+
git push origin "v$version"
|
15
|
+
git push legacy
|
16
|
+
git push legacy "v$version"
|
17
|
+
gem push pkg/*-${version}.gem
|
18
|
+
|
data/script/test
ADDED
data/test/agent_test.rb
CHANGED
@@ -2,6 +2,16 @@ require File.expand_path("../helper", __FILE__)
|
|
2
2
|
|
3
3
|
module Sawyer
|
4
4
|
class AgentTest < TestCase
|
5
|
+
|
6
|
+
class InlineRelsParser
|
7
|
+
def parse(data)
|
8
|
+
links = {}
|
9
|
+
data.keys.select {|k| k[/_url$/] }.each {|k| links[k.to_s.gsub(/_url$/, '')] = data.delete(k) }
|
10
|
+
|
11
|
+
return data, links
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
5
15
|
def setup
|
6
16
|
@stubs = Faraday::Adapter::Test::Stubs.new
|
7
17
|
@agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
@@ -25,6 +35,31 @@ module Sawyer
|
|
25
35
|
assert_equal :get, @agent.rels[:users].method
|
26
36
|
end
|
27
37
|
|
38
|
+
def test_allows_custom_rel_parsing
|
39
|
+
@stubs.get '/a/' do |env|
|
40
|
+
assert_equal 'foo.com', env[:url].host
|
41
|
+
|
42
|
+
[200, {}, Sawyer::Agent.encode(
|
43
|
+
:url => '/',
|
44
|
+
:users_url => '/users',
|
45
|
+
:repos_url => '/repos')]
|
46
|
+
end
|
47
|
+
|
48
|
+
agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
49
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
50
|
+
conn.adapter :test, @stubs
|
51
|
+
end
|
52
|
+
agent.links_parser = InlineRelsParser.new
|
53
|
+
|
54
|
+
assert_equal 200, agent.root.status
|
55
|
+
|
56
|
+
assert_equal '/users', agent.rels[:users].href
|
57
|
+
assert_equal :get, agent.rels[:users].method
|
58
|
+
assert_equal '/repos', agent.rels[:repos].href
|
59
|
+
assert_equal :get, agent.rels[:repos].method
|
60
|
+
|
61
|
+
end
|
62
|
+
|
28
63
|
def test_saves_root_endpoint
|
29
64
|
@stubs.get '/a/' do |env|
|
30
65
|
[200, {}, '{}']
|
@@ -79,6 +114,26 @@ module Sawyer
|
|
79
114
|
:query => {:foo => 'bar'}
|
80
115
|
assert_equal 200, res.status
|
81
116
|
end
|
117
|
+
|
118
|
+
def test_encodes_and_decodes_times
|
119
|
+
time = Time.at(Time.now.to_i)
|
120
|
+
data = {:a => 1, :b => true, :c => 'c', :created_at => time, :published_at => nil}
|
121
|
+
data = [data.merge(:foo => [data])]
|
122
|
+
encoded = Sawyer::Agent.encode(data)
|
123
|
+
decoded = Sawyer::Agent.decode(encoded)
|
124
|
+
|
125
|
+
2.times do
|
126
|
+
assert_equal 1, decoded.size
|
127
|
+
decoded = decoded.shift
|
128
|
+
|
129
|
+
assert_equal 1, decoded[:a]
|
130
|
+
assert_equal true, decoded[:b]
|
131
|
+
assert_equal 'c', decoded[:c]
|
132
|
+
assert_equal time, decoded[:created_at]
|
133
|
+
assert_nil decoded[:published_at]
|
134
|
+
decoded = decoded[:foo]
|
135
|
+
end
|
136
|
+
end
|
82
137
|
end
|
83
138
|
end
|
84
139
|
|
data/test/relation_test.rb
CHANGED
@@ -28,6 +28,23 @@ module Sawyer
|
|
28
28
|
assert_kind_of URITemplate, rel.href_template
|
29
29
|
end
|
30
30
|
|
31
|
+
def test_builds_rels_from_hash
|
32
|
+
index = {
|
33
|
+
'self' => '/users/1'
|
34
|
+
}
|
35
|
+
|
36
|
+
rels = Sawyer::Relation.from_links(nil, index)
|
37
|
+
|
38
|
+
assert_equal 1, rels.size
|
39
|
+
assert_equal [:self], rels.keys
|
40
|
+
assert rel = rels[:self]
|
41
|
+
assert_equal :self, rel.name
|
42
|
+
assert_equal '/users/1', rel.href
|
43
|
+
assert_equal :get, rel.method
|
44
|
+
assert_equal [:get], rel.available_methods.to_a
|
45
|
+
assert_kind_of URITemplate, rel.href_template
|
46
|
+
end
|
47
|
+
|
31
48
|
def test_builds_rels_from_hash_index
|
32
49
|
index = {
|
33
50
|
'self' => {:href => '/users/1'}
|
@@ -109,6 +126,41 @@ module Sawyer
|
|
109
126
|
assert_equal 404, rel.get.status
|
110
127
|
assert_equal 200, rel.get(:uri => {'user' => 'octocat', 'repo' => 'hello', 'a' => 1, 'b' => 2}).status
|
111
128
|
end
|
129
|
+
|
130
|
+
def test_handles_invalid_uri
|
131
|
+
hash = {:href => '/this has spaces', :method => 'post'}
|
132
|
+
rel = Sawyer::Relation.from_link(nil, :self, hash)
|
133
|
+
|
134
|
+
assert_equal :self, rel.name
|
135
|
+
assert_equal '/this has spaces', rel.href
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_allows_all_methods_when_not_in_strict_mode
|
139
|
+
|
140
|
+
agent = Sawyer::Agent.new "http://foo.com/a/", :allow_undefined_methods => true do |conn|
|
141
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
142
|
+
conn.adapter :test do |stubs|
|
143
|
+
stubs.get '/a/1' do
|
144
|
+
[200, {}, '{}']
|
145
|
+
end
|
146
|
+
stubs.delete '/a/1' do
|
147
|
+
[204, {}, '{}']
|
148
|
+
end
|
149
|
+
stubs.post '/a/1' do
|
150
|
+
[200, {}, '{}']
|
151
|
+
end
|
152
|
+
stubs.put '/a/1' do
|
153
|
+
[204, {}, '{}']
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
rel = Sawyer::Relation.new agent, :self, "/a/1"
|
159
|
+
assert_equal 200, rel.get.status
|
160
|
+
assert_equal 200, rel.post.status
|
161
|
+
assert_equal 204, rel.put.status
|
162
|
+
assert_equal 204, rel.delete.status
|
163
|
+
end
|
112
164
|
end
|
113
165
|
end
|
114
166
|
|
data/test/resource_test.rb
CHANGED
@@ -2,19 +2,28 @@ require File.expand_path("../helper", __FILE__)
|
|
2
2
|
|
3
3
|
module Sawyer
|
4
4
|
class ResourceTest < TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
8
|
+
@agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
9
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
10
|
+
conn.adapter :test, @stubs
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
def test_accessible_keys
|
6
|
-
res = Resource.new
|
15
|
+
res = Resource.new @agent, :a => 1,
|
7
16
|
:_links => {:self => {:href => '/'}}
|
8
17
|
|
9
18
|
assert_equal 1, res.a
|
10
19
|
assert res.rels[:self]
|
11
|
-
assert_equal
|
20
|
+
assert_equal @agent, res.agent
|
12
21
|
assert_equal 1, res.fields.size
|
13
22
|
assert res.fields.include?(:a)
|
14
23
|
end
|
15
24
|
|
16
25
|
def test_clashing_keys
|
17
|
-
res = Resource.new
|
26
|
+
res = Resource.new @agent, :agent => 1, :rels => 2, :fields => 3,
|
18
27
|
:_links => {:self => {:href => '/'}}
|
19
28
|
|
20
29
|
assert_equal 1, res.agent
|
@@ -22,7 +31,7 @@ module Sawyer
|
|
22
31
|
assert_equal 3, res.fields
|
23
32
|
|
24
33
|
assert res._rels[:self]
|
25
|
-
assert_equal
|
34
|
+
assert_equal @agent, res._agent
|
26
35
|
assert_equal 3, res._fields.size
|
27
36
|
[:agent, :rels, :fields].each do |f|
|
28
37
|
assert res._fields.include?(f)
|
@@ -30,7 +39,7 @@ module Sawyer
|
|
30
39
|
end
|
31
40
|
|
32
41
|
def test_nested_object
|
33
|
-
res = Resource.new
|
42
|
+
res = Resource.new @agent,
|
34
43
|
:user => {:id => 1, :_links => {:self => {:href => '/users/1'}}},
|
35
44
|
:_links => {:self => {:href => '/'}}
|
36
45
|
|
@@ -41,7 +50,7 @@ module Sawyer
|
|
41
50
|
end
|
42
51
|
|
43
52
|
def test_nested_collection
|
44
|
-
res = Resource.new
|
53
|
+
res = Resource.new @agent,
|
45
54
|
:users => [{:id => 1, :_links => {:self => {:href => '/users/1'}}}],
|
46
55
|
:_links => {:self => {:href => '/'}}
|
47
56
|
|
@@ -55,7 +64,7 @@ module Sawyer
|
|
55
64
|
end
|
56
65
|
|
57
66
|
def test_attribute_predicates
|
58
|
-
res = Resource.new
|
67
|
+
res = Resource.new @agent, :a => 1, :b => true, :c => nil, :d => false
|
59
68
|
|
60
69
|
assert res.a?
|
61
70
|
assert res.b?
|
@@ -64,7 +73,7 @@ module Sawyer
|
|
64
73
|
end
|
65
74
|
|
66
75
|
def test_attribute_setter
|
67
|
-
res = Resource.new
|
76
|
+
res = Resource.new @agent, :a => 1
|
68
77
|
assert_equal 1, res.a
|
69
78
|
assert !res.key?(:b)
|
70
79
|
|
@@ -74,10 +83,10 @@ module Sawyer
|
|
74
83
|
end
|
75
84
|
|
76
85
|
def test_dynamic_attribute_methods_from_getter
|
77
|
-
res = Resource.new
|
78
|
-
assert
|
79
|
-
assert
|
80
|
-
assert
|
86
|
+
res = Resource.new @agent, :a => 1
|
87
|
+
assert res.key?(:a)
|
88
|
+
assert res.respond_to?(:a)
|
89
|
+
assert res.respond_to?(:a=)
|
81
90
|
|
82
91
|
assert_equal 1, res.a
|
83
92
|
assert res.respond_to?(:a)
|
@@ -85,7 +94,7 @@ module Sawyer
|
|
85
94
|
end
|
86
95
|
|
87
96
|
def test_dynamic_attribute_methods_from_setter
|
88
|
-
res = Resource.new
|
97
|
+
res = Resource.new @agent, :a => 1
|
89
98
|
assert !res.key?(:b)
|
90
99
|
assert !res.respond_to?(:b)
|
91
100
|
assert !res.respond_to?(:b=)
|
@@ -95,5 +104,19 @@ module Sawyer
|
|
95
104
|
assert res.respond_to?(:b)
|
96
105
|
assert res.respond_to?(:b=)
|
97
106
|
end
|
107
|
+
|
108
|
+
def test_attrs
|
109
|
+
res = Resource.new @agent, :a => 1
|
110
|
+
hash = {:a => 1 }
|
111
|
+
assert_equal hash, res.attrs
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_handle_hash_notation_with_string_key
|
115
|
+
res = Resource.new @agent, :a => 1
|
116
|
+
assert_equal 1, res['a']
|
117
|
+
|
118
|
+
res[:b] = 2
|
119
|
+
assert_equal 2, res.b
|
120
|
+
end
|
98
121
|
end
|
99
122
|
end
|
data/test/response_test.rb
CHANGED
@@ -16,6 +16,11 @@ module Sawyer
|
|
16
16
|
}
|
17
17
|
)]
|
18
18
|
end
|
19
|
+
|
20
|
+
stub.get '/emails' do
|
21
|
+
emails = %w(rick@example.com technoweenie@example.com)
|
22
|
+
[200, {'Content-Type' => 'application/json'}, Sawyer::Agent.encode(emails)]
|
23
|
+
end
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
@@ -55,6 +60,11 @@ module Sawyer
|
|
55
60
|
assert_equal 201, res.status
|
56
61
|
assert_nil res.data
|
57
62
|
end
|
63
|
+
|
64
|
+
def test_handles_arrays_of_strings
|
65
|
+
res = @agent.call(:get, '/emails')
|
66
|
+
assert_equal 'rick@example.com', res.data.first
|
67
|
+
end
|
58
68
|
end
|
59
69
|
end
|
60
70
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sawyer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.5.0
|
46
|
-
description:
|
46
|
+
description:
|
47
47
|
email: technoweenie@gmail.com
|
48
48
|
executables: []
|
49
49
|
extensions: []
|
@@ -53,25 +53,26 @@ files:
|
|
53
53
|
- LICENSE.md
|
54
54
|
- README.md
|
55
55
|
- Rakefile
|
56
|
-
-
|
57
|
-
- example/client.rb
|
58
|
-
- example/nigiri.schema.json
|
59
|
-
- example/server.rb
|
60
|
-
- example/user.schema.json
|
61
|
-
- lib/sawyer.rb
|
56
|
+
- sawyer.gemspec
|
62
57
|
- lib/sawyer/agent.rb
|
58
|
+
- lib/sawyer/hal_rels_parser.rb
|
63
59
|
- lib/sawyer/relation.rb
|
64
60
|
- lib/sawyer/resource.rb
|
65
61
|
- lib/sawyer/response.rb
|
66
62
|
- lib/sawyer/serializer.rb
|
67
|
-
- sawyer.
|
63
|
+
- lib/sawyer.rb
|
68
64
|
- test/agent_test.rb
|
69
65
|
- test/helper.rb
|
70
66
|
- test/relation_test.rb
|
71
67
|
- test/resource_test.rb
|
72
68
|
- test/response_test.rb
|
73
|
-
|
74
|
-
|
69
|
+
- script/console
|
70
|
+
- script/package
|
71
|
+
- script/release
|
72
|
+
- script/test
|
73
|
+
homepage: https://github.com/lostisland/sawyer
|
74
|
+
licenses:
|
75
|
+
- MIT
|
75
76
|
post_install_message:
|
76
77
|
rdoc_options: []
|
77
78
|
require_paths:
|
@@ -82,23 +83,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
83
|
- - ! '>='
|
83
84
|
- !ruby/object:Gem::Version
|
84
85
|
version: '0'
|
85
|
-
segments:
|
86
|
-
- 0
|
87
|
-
hash: -3466190819871303014
|
88
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
87
|
none: false
|
90
88
|
requirements:
|
91
89
|
- - ! '>='
|
92
90
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
91
|
+
version: 1.3.5
|
94
92
|
requirements: []
|
95
|
-
rubyforge_project:
|
93
|
+
rubyforge_project:
|
96
94
|
rubygems_version: 1.8.23
|
97
95
|
signing_key:
|
98
96
|
specification_version: 2
|
99
97
|
summary: Secret User Agent of HTTP
|
100
|
-
test_files:
|
101
|
-
|
102
|
-
- test/relation_test.rb
|
103
|
-
- test/resource_test.rb
|
104
|
-
- test/response_test.rb
|
98
|
+
test_files: []
|
99
|
+
has_rdoc:
|
data/SPEC.md
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# Sawyer
|
2
|
-
|
3
|
-
To use Sawyer, create a new Agent with a URL endpoint.
|
4
|
-
|
5
|
-
```ruby
|
6
|
-
endpoint = "http://my-api.com"
|
7
|
-
agent = Sawyer::Agent.new endpoint do |conn|
|
8
|
-
conn.headers['content-type'] = 'application/vnd.my-api+json'
|
9
|
-
end
|
10
|
-
```
|
11
|
-
|
12
|
-
From here, we can access the root to get the initial actions.
|
13
|
-
|
14
|
-
```ruby
|
15
|
-
root = agent.start
|
16
|
-
```
|
17
|
-
|
18
|
-
Every request in Sawyer returns a Sawyer::Response. It's very similar
|
19
|
-
to a raw Faraday::Response, with a few extra methods.
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
# HTTP Headers
|
23
|
-
root.headers
|
24
|
-
|
25
|
-
# HTTP status
|
26
|
-
root.status
|
27
|
-
|
28
|
-
# The JSON Schema
|
29
|
-
root.schema
|
30
|
-
|
31
|
-
# The link relations
|
32
|
-
root.rels
|
33
|
-
|
34
|
-
# The contents (probably empty from the root)
|
35
|
-
root.data
|
36
|
-
```
|
37
|
-
|
38
|
-
Now, we can access a relation off the root resource.
|
39
|
-
|
40
|
-
```ruby
|
41
|
-
resource = root.data
|
42
|
-
res = resource.rels[:users].call :query => {:sort => 'login'}
|
43
|
-
|
44
|
-
# An array of users
|
45
|
-
users = res.data
|
46
|
-
```
|
47
|
-
|
48
|
-
This call returns two types of relations: relations on the collection of
|
49
|
-
users, and relations on each user. You can access the collection
|
50
|
-
resources from the response.
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
# get the next page of users
|
54
|
-
res2 = res.rels[:next].call
|
55
|
-
|
56
|
-
# page 2 of the users collection
|
57
|
-
res2.data
|
58
|
-
```
|
59
|
-
|
60
|
-
Each user has it's own relations too:
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
# favorite the previous user
|
64
|
-
users.each_with_index do |user, index|
|
65
|
-
res = user.rels[:favorite].call users[index-1]
|
66
|
-
if !res.success?
|
67
|
-
puts "#{user.name} could not favorite #{users[index-1].name}"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
```
|
71
|
-
|
data/example/client.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
require File.expand_path("../../lib/sawyer", __FILE__)
|
2
|
-
require 'faraday'
|
3
|
-
require 'pp'
|
4
|
-
|
5
|
-
endpoint = "http://localhost:9393/"
|
6
|
-
agent = Sawyer::Agent.new(endpoint) do |http|
|
7
|
-
http.headers['content-type'] = 'application/json'
|
8
|
-
end
|
9
|
-
puts agent.inspect
|
10
|
-
puts
|
11
|
-
|
12
|
-
root = agent.start
|
13
|
-
puts root.inspect
|
14
|
-
|
15
|
-
puts "LISTING USERS"
|
16
|
-
users_rel = root.data.rels[:users]
|
17
|
-
|
18
|
-
puts users_rel.inspect
|
19
|
-
puts
|
20
|
-
|
21
|
-
users_res = users_rel.get
|
22
|
-
puts users_res.inspect
|
23
|
-
|
24
|
-
users = users_res.data
|
25
|
-
|
26
|
-
users.each do |user|
|
27
|
-
puts "#{user.login} favorites:"
|
28
|
-
|
29
|
-
fav_res = user.rels[:favorites].get
|
30
|
-
|
31
|
-
fav_res.data.each do |sushi|
|
32
|
-
puts "- #{sushi.inspect})"
|
33
|
-
end
|
34
|
-
puts
|
35
|
-
end
|
36
|
-
|
37
|
-
puts "CREATING USER"
|
38
|
-
create_user_rel = root.data.rels[:users]
|
39
|
-
|
40
|
-
puts create_user_rel.inspect
|
41
|
-
|
42
|
-
created = create_user_rel.post(:login => 'booya')
|
43
|
-
puts created.inspect
|
44
|
-
puts
|
45
|
-
|
46
|
-
puts "ADD A FAVORITE"
|
47
|
-
created_user = created.data
|
48
|
-
create_fav_res = created_user.rels[:favorites].post nil, :query => {:id => 1}
|
49
|
-
puts create_fav_res.inspect
|
50
|
-
|
data/example/nigiri.schema.json
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"type": "object",
|
3
|
-
"relations": [
|
4
|
-
{"rel": "all", "href": "/nigiri"}
|
5
|
-
],
|
6
|
-
"properties": {
|
7
|
-
"id": {
|
8
|
-
"type": "integer",
|
9
|
-
"minimum": 1,
|
10
|
-
"readonly": true
|
11
|
-
},
|
12
|
-
"name": {
|
13
|
-
"type": "string"
|
14
|
-
},
|
15
|
-
"fish": {
|
16
|
-
"type": "string"
|
17
|
-
},
|
18
|
-
"_links": {
|
19
|
-
"type": "array",
|
20
|
-
"items": {
|
21
|
-
"type": "object",
|
22
|
-
"properties": {
|
23
|
-
"rel": {
|
24
|
-
"type": "string"
|
25
|
-
},
|
26
|
-
"href": {
|
27
|
-
"type": "string",
|
28
|
-
"optional": true
|
29
|
-
},
|
30
|
-
"method": {
|
31
|
-
"type": "string",
|
32
|
-
"default": "get"
|
33
|
-
},
|
34
|
-
"schema": {
|
35
|
-
"type": "string",
|
36
|
-
"optional": true
|
37
|
-
}
|
38
|
-
},
|
39
|
-
"additionalProperties": false
|
40
|
-
},
|
41
|
-
"additionalProperties": false,
|
42
|
-
"readonly": true
|
43
|
-
}
|
44
|
-
}
|
45
|
-
}
|
46
|
-
|
47
|
-
|
data/example/server.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
require 'sinatra'
|
2
|
-
require 'yajl'
|
3
|
-
|
4
|
-
get '/' do
|
5
|
-
app_type
|
6
|
-
|
7
|
-
Yajl.dump({
|
8
|
-
:_links => {
|
9
|
-
:users => {:href => "/users", :method => 'get,post'},
|
10
|
-
:nigiri => {:href => "/nigiri"}
|
11
|
-
}
|
12
|
-
}, :pretty => true)
|
13
|
-
end
|
14
|
-
|
15
|
-
def app_type
|
16
|
-
content_type "application/vnd.sushihub+json"
|
17
|
-
end
|
18
|
-
|
19
|
-
users = [
|
20
|
-
{:id => 1, :login => 'sawyer', :created_at => Time.utc(2004, 9, 22),
|
21
|
-
:_links => {
|
22
|
-
:self => {:href => '/users/sawyer'},
|
23
|
-
:favorites => {:href => '/users/sawyer/favorites', :method => 'get,post'}
|
24
|
-
}},
|
25
|
-
{:id => 2, :login => 'faraday', :created_at => Time.utc(2004, 12, 22),
|
26
|
-
:_links => {
|
27
|
-
:self => {:href => '/users/faraday'},
|
28
|
-
:favorites => {:href => '/users/faraday/favorites', :method => 'get,post'}
|
29
|
-
}}
|
30
|
-
]
|
31
|
-
|
32
|
-
nigiri = [
|
33
|
-
{:id => 1, :name => 'sake', :fish => 'salmon',
|
34
|
-
:_links => {
|
35
|
-
:self => {:href => '/nigiri/sake'}
|
36
|
-
}},
|
37
|
-
{:id => 2, :name => 'unagi', :fish => 'eel',
|
38
|
-
:_links => {
|
39
|
-
:self => {:href => '/nigiri/unagi'}
|
40
|
-
}}
|
41
|
-
]
|
42
|
-
|
43
|
-
get '/users' do
|
44
|
-
app_type
|
45
|
-
|
46
|
-
Yajl.dump users, :pretty => true
|
47
|
-
end
|
48
|
-
|
49
|
-
new_users = {}
|
50
|
-
post '/users' do
|
51
|
-
if env['CONTENT_TYPE'].to_s !~ /json/i
|
52
|
-
halt 400, "Needs JSON"
|
53
|
-
end
|
54
|
-
|
55
|
-
app_type
|
56
|
-
|
57
|
-
hash = Yajl.load request.body.read, :symbolize_keys => true
|
58
|
-
new_users[hash[:login]] = hash
|
59
|
-
|
60
|
-
headers "Location" => "/users/#{hash[:login]}"
|
61
|
-
status 201
|
62
|
-
Yajl.dump hash.update(
|
63
|
-
:id => 3,
|
64
|
-
:created_at => Time.now.utc.xmlschema,
|
65
|
-
:_links => {
|
66
|
-
:self => {:href => "/users/#{hash[:login]}"},
|
67
|
-
:favorites => {:href => "/users/#{hash[:login]}/favorites", :method => 'get,post'}
|
68
|
-
}
|
69
|
-
), :pretty => true
|
70
|
-
end
|
71
|
-
|
72
|
-
get '/users/:login' do
|
73
|
-
headers 'Content-Type' => app_type
|
74
|
-
if hash = users.detect { |u| u[:login] == params[:login] }
|
75
|
-
Yajl.dump hash, :pretty => true
|
76
|
-
else
|
77
|
-
halt 404
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
get '/users/:login/favorites' do
|
82
|
-
app_type
|
83
|
-
|
84
|
-
case params[:login]
|
85
|
-
when users[0][:login] then Yajl.dump([nigiri[0]], :pretty => true)
|
86
|
-
when users[1][:login] then Yajl.dump([], :pretty => true)
|
87
|
-
else halt 404
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
post '/users/:login/favorites' do
|
92
|
-
if params[:id].to_i > 0
|
93
|
-
halt 201
|
94
|
-
else
|
95
|
-
halt 422
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
get '/nigiri' do
|
100
|
-
app_type
|
101
|
-
|
102
|
-
Yajl.dump nigiri, :pretty => true
|
103
|
-
end
|
104
|
-
|
105
|
-
get '/nigiri/:name' do
|
106
|
-
app_type
|
107
|
-
|
108
|
-
if hash = nigiri.detect { |n| n[:name] == params[:name] }
|
109
|
-
Yajl.dump hash, :pretty => true
|
110
|
-
else
|
111
|
-
halt(404)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
data/example/user.schema.json
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"type": "object",
|
3
|
-
"relations": [
|
4
|
-
{"rel": "all", "href": "/users"},
|
5
|
-
{"rel": "create", "href": "/users", "method": "post"},
|
6
|
-
{"rel": "favorites", "schema": "/schema/nigiri"},
|
7
|
-
{"rel": "favorites/create", "method": "post"}
|
8
|
-
],
|
9
|
-
"properties": {
|
10
|
-
"id": {
|
11
|
-
"type": "integer",
|
12
|
-
"minimum": 1,
|
13
|
-
"readonly": true
|
14
|
-
},
|
15
|
-
"login": {
|
16
|
-
"type": "string"
|
17
|
-
},
|
18
|
-
"created_at": {
|
19
|
-
"type": "string",
|
20
|
-
"pattern": "\\d{8}T\\d{6}Z",
|
21
|
-
"readonly": true
|
22
|
-
},
|
23
|
-
"_links": {
|
24
|
-
"type": "array",
|
25
|
-
"items": {
|
26
|
-
"type": "object",
|
27
|
-
"properties": {
|
28
|
-
"rel": {
|
29
|
-
"type": "string"
|
30
|
-
},
|
31
|
-
"href": {
|
32
|
-
"type": "string",
|
33
|
-
"optional": true
|
34
|
-
},
|
35
|
-
"method": {
|
36
|
-
"type": "string",
|
37
|
-
"default": "get"
|
38
|
-
},
|
39
|
-
"schema": {
|
40
|
-
"type": "string",
|
41
|
-
"optional": true
|
42
|
-
}
|
43
|
-
},
|
44
|
-
"additionalProperties": false
|
45
|
-
},
|
46
|
-
"additionalProperties": false,
|
47
|
-
"readonly": true
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|