hyper_navigator 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 153aa069f454992869ca73604cfd58b63abceb83
4
- data.tar.gz: 8753b08c926a28fd9e29bf7b401ccab668340245
3
+ metadata.gz: 0f83d77d20b8bbb093c8a1cc3e32a49e1079ebcb
4
+ data.tar.gz: cae40da73f186e5ce41d4a9f07dd9551bcce4695
5
5
  SHA512:
6
- metadata.gz: a69db4f2c27ca273580b5eceab5535a7591e866bcba43f066cf0d87da3593450ac60dd90abc3384da5e6910f2d9054fe8a6d0a39e8d966710306bbb2ce7c9481
7
- data.tar.gz: 42a615509a65e5b9fb46ef341feaebfa9f53b0ad4573ac2cb9b5e16a416f8feb6aaa3d1d21bcd6e7fa162258446eb91dd0f8029081b30e72e2fbb8604ab0042d
6
+ metadata.gz: 42fb7fc920df883e1965f6c316086812a2f0daafbcaab385091d163cbc801bcd8bfdebcd115a80f4dd6f5dbabbd01d3b87b17dff598ea07324e6933868a7c62f
7
+ data.tar.gz: c44fe57cf3cf77c4c769e6222d0d6c5b6d666cc90512ab9e1c163584aa549c3202abaf44b8e2bae38177802c9ae508fe0beb3dd5567ecc3d13cb077e8126c24a
data/README.md CHANGED
@@ -21,17 +21,17 @@ It expects each resource to return a structure in the given format:
21
21
  The main entry point for this gem is
22
22
 
23
23
  ```ruby
24
- HyperNavigator.surf(url, traversal-path)
24
+ HyperNavigator.surf(href, traversal_pattern)
25
25
  ```
26
26
 
27
- The `traversal-path` argument provided is an array of `rel` names. This array should contain the rel names in order of traversal.
27
+ The `traversal_pattern` argument provided is an array of `rel` names. This array should contain the rel names in order of traversal.
28
28
 
29
29
  An example path, that will look for an `apple` rel in the resource, fetch from it's corresponding href, then look for a `pudding` rel in that resource and fetch its corresponding href:
30
30
 
31
31
  ```ruby
32
- traversal-path = ["apple", "pudding"]
32
+ traversal_pattern = ["apple", "pudding"]
33
33
 
34
- HyperNavigator.surf("https://fruitful-resources.io", traversal-path)
34
+ HyperNavigator.surf("https://fruitful-resources.io", traversal_pattern)
35
35
  ```
36
36
 
37
37
  The return value of `#surf` will be a `HyperNavigator::Node`.
@@ -61,7 +61,6 @@ Or install it yourself as:
61
61
  ## Usage
62
62
 
63
63
  `#surf` will return all nodes encountered during a browse.
64
- `#surf_to_leaves` will return just the leaf nodes during a browse.
65
64
 
66
65
  Example usage:
67
66
 
@@ -71,9 +70,17 @@ Example usage:
71
70
  path = ["apple", "pudding"]
72
71
  headers = { "Authorization": "Bearer #{$token}" }
73
72
 
74
- result = HyperNavigator.surf_to_leaves('https://fruitful-resources.io', path, headers)
73
+ result = HyperNavigator.surf("https://fruitful-resources.io", traversal_pattern, headers)
75
74
  ```
76
75
 
76
+ ### Pattern Matching
77
+
78
+ The traversal pattern can contain the special matching symbols `:any` and `:star`.
79
+ They are analogous to the regular expression metacharacters `.` and `*`.
80
+
81
+ `:any` will match any rel.
82
+ `:star` matches the preceding element zero or more times.
83
+
77
84
  ## Development
78
85
 
79
86
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/examples/surf.rb ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "optparse"
5
+ require "ostruct"
6
+ require "hyper_navigator"
7
+ require "pp"
8
+
9
+ options = OpenStruct.new
10
+ options.verbose = false
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner ="Usage: #{ARGV[0]} [options]"
14
+ opts.on("-uURL", "--root-url=URL", "required root URL") do |url|
15
+ options.url = url
16
+ end
17
+ opts.on("-pPATTERN", "--pattern=PATTERN", "required traversal pattern") do |pattern|
18
+ options.pattern = eval(pattern)
19
+ end
20
+ opts.on("-a[AUTH_URL]", "--auth-url=[AUTH_URL]") do |url|
21
+ options.auth_url = url
22
+ end
23
+ opts.on("-v", "--verbose") do |v|
24
+ options.verbose = true
25
+ end
26
+ end.parse!
27
+
28
+ CLIENT_ID=ENV['CLIENT_ID']
29
+ CLIENT_SECRET=ENV['CLIENT_SECRET']
30
+
31
+ if options.auth_url
32
+ headers = {"Content-type" => "application/x-www-form-urlencoded"}
33
+ body = URI.encode_www_form(
34
+ "client_id" => CLIENT_ID,
35
+ "client_secret" => CLIENT_SECRET,
36
+ "grant_type" => "client_credentials",
37
+ "response_type" => "code")
38
+ response = HyperNavigator.post(options.auth_url, body, headers)
39
+ json = JSON.parse(response.body)
40
+ $token = json['id_token']
41
+ options.headers = { "Authorization" => "Bearer #{$token}" }
42
+ else
43
+ options.headers = {}
44
+ end
45
+
46
+ result = HyperNavigator.surf(options.url, options.pattern, options.headers, { :verbose => options.verbose })
47
+
48
+ result.map do |n|
49
+ pp JSON.parse(n.response.body) rescue nil
50
+ end
@@ -3,24 +3,8 @@ require "hyper_navigator/node"
3
3
 
4
4
  module HyperNavigator
5
5
 
6
- def self.surf(home, path=nil, headers={})
7
- root = Node.new('rel', home, nil, 0, path, headers)
8
- nodes = lambda do |node|
9
- node.descendants + node.descendants.flat_map {|d| nodes.call(d) }
10
- end
11
- nodes.call(root)
12
- end
13
-
14
- def self.surf_to_leaves(home, path=nil, headers={})
15
- root = Node.new('rel', home, nil, 0, path, headers)
16
- nodes = lambda do |node|
17
- if node.descendants.empty?
18
- node
19
- else
20
- node.descendants.flat_map {|d| nodes.call(d) }
21
- end
22
- end
23
- nodes.call(root)
6
+ def self.surf(root_url, exp, headers={}, options={})
7
+ PatternMatcher.new(headers, options).match(root_url, exp).flatten_branch
24
8
  end
25
9
 
26
10
  end
@@ -25,46 +25,105 @@ module HyperNavigator
25
25
  http.request(request)
26
26
  end
27
27
 
28
- class Node
29
-
30
- attr_reader :ancestor, :descendants, :rel, :href, :depth, :response, :path
28
+ class PatternMatcher
31
29
 
32
- def initialize(rel, href, ancestor=nil, depth=nil, path=nil, headers={})
33
- @ancestor = ancestor
34
- @descendants = []
35
- @rel = rel
36
- @href = href
37
- @depth = depth
38
- @path = path
30
+ def initialize(headers,opts={})
31
+ @opts = opts
39
32
  @headers = headers
33
+ end
40
34
 
41
- @response = HyperNavigator.get(href, headers)
42
- @descendants = follow_links
35
+ def match(href, exp)
36
+ match_here(exp, Node.new(:root, href, @headers, 0))
43
37
  end
44
38
 
45
- private
39
+ def match_here(exp, node)
40
+ if exp == nil
41
+ return node
42
+ elsif exp[1] == :star
43
+ match_star(exp[0], exp.drop(2), node)
44
+ return node
45
+ elsif exp[0] == :any
46
+ match_here_descendants(exp, node)
47
+ return node
48
+ elsif exp[0] == node.rel
49
+ match_here_descendants(exp, node)
50
+ return node
51
+ end
52
+ return NullNode.new
53
+ end
46
54
 
47
- def links
48
- json = JSON.parse(@response.body) rescue nil
49
- return [] unless json
50
- # If we've been given a path then only follow the links in the path
51
- if @path
52
- json["links"].select { |link| link["rel"] == @path.first }
55
+ def match_here_descendants(exp, node)
56
+ links = node.links.map {|link| make_node(link, node.depth + 1)}
57
+ descendants = links.map {|n| match_here(exp.drop(1), n) }
58
+ node.descendants = descendants.select {|d| d.class == Node }
59
+ end
60
+
61
+ def match_star(exp_star, exp, node)
62
+ # in case of zero matches, exp can match here
63
+ node_here = match_here(exp, node)
64
+
65
+ if node_here.is_a? NullNode
66
+ match_star_descendants(exp_star, exp, node)
67
+ end
68
+ end
69
+
70
+ def match_star_descendants(exp_star, exp, node)
71
+ if exp_star == :any
72
+ links = node.links
53
73
  else
54
- json["links"].reject {|i| ["self","up","next","prev"].any?{|r| r == i["rel"]} }
74
+ links = node.links.select { |link| link["rel"] == exp_star }
55
75
  end
76
+
77
+ node.descendants = links.map { |link| make_node(link, node.depth + 1) }
78
+ node.descendants.map {|desc| match_star(exp_star, exp, desc) }
56
79
  end
57
80
 
58
- def next_step
59
- return @path.drop(1) if @path
60
- nil
81
+ def make_node(link, depth=nil)
82
+ padding = ' ' * depth
83
+ puts "#{padding}#{link}" if @opts[:verbose]
84
+ Node.new(link["rel"], link["href"], @headers, depth)
61
85
  end
62
86
 
63
- def follow_links
64
- links.map do |link|
65
- Node.new(link["rel"], link["href"], self, @depth + 1, next_step, @headers)
87
+ end
88
+
89
+ class Node
90
+
91
+ IGNORE_REFS = ["self", "up", "next", "prev"]
92
+
93
+ attr_reader :rel, :href, :headers, :response
94
+ attr_accessor :descendants, :depth
95
+
96
+ def initialize(rel, href, headers={}, depth=nil)
97
+ @rel = rel
98
+ @href = href
99
+ @headers = headers
100
+ @descendants = []
101
+ @depth = depth
102
+ if href
103
+ @response = HyperNavigator.get(href, headers)
104
+ raise RuntimeError, @response unless @response.code =~ /^2..$/
66
105
  end
67
106
  end
68
107
 
108
+ def links
109
+ @cached_links ||= begin
110
+ json = JSON.parse(@response.body) rescue nil
111
+ return [] unless json
112
+ json["links"].reject do |i|
113
+ IGNORE_REFS.any? { |r| r == i["rel"] }
114
+ end
115
+ end
116
+ end
117
+
118
+ def flatten_branch
119
+ descendants + descendants.flat_map { | d| d.flatten_branch }
120
+ end
121
+
122
+ end
123
+
124
+ class NullNode < Node
125
+ def initialize()
126
+ super(nil, nil)
127
+ end
69
128
  end
70
- end
129
+ end
@@ -1,3 +1,3 @@
1
1
  module HyperNavigator
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyper_navigator
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
  - Carl Douglas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-21 00:00:00.000000000 Z
11
+ date: 2018-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,6 +70,7 @@ files:
70
70
  - Rakefile
71
71
  - bin/console
72
72
  - bin/setup
73
+ - examples/surf.rb
73
74
  - hyper_navigator.gemspec
74
75
  - lib/hyper_navigator.rb
75
76
  - lib/hyper_navigator/node.rb