ae_easy-core 0.0.3
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 +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +57 -0
- data/LICENSE +21 -0
- data/README.md +16 -0
- data/Rakefile +22 -0
- data/ae_easy-core.gemspec +49 -0
- data/doc/AeEasy.html +117 -0
- data/doc/AeEasy/Core.html +1622 -0
- data/doc/AeEasy/Core/Config.html +311 -0
- data/doc/AeEasy/Core/Exception.html +117 -0
- data/doc/AeEasy/Core/Exception/OutdatedError.html +135 -0
- data/doc/AeEasy/Core/Helper.html +117 -0
- data/doc/AeEasy/Core/Helper/Cookie.html +1070 -0
- data/doc/AeEasy/Core/Mock.html +282 -0
- data/doc/AeEasy/Core/Mock/FakeDb.html +2316 -0
- data/doc/AeEasy/Core/Mock/FakeExecutor.html +2226 -0
- data/doc/AeEasy/Core/Mock/FakeParser.html +275 -0
- data/doc/AeEasy/Core/Mock/FakeSeeder.html +269 -0
- data/doc/AeEasy/Core/Plugin.html +117 -0
- data/doc/AeEasy/Core/Plugin/CollectionVault.html +299 -0
- data/doc/AeEasy/Core/Plugin/ConfigBehavior.html +541 -0
- data/doc/AeEasy/Core/Plugin/ContextIntegrator.html +445 -0
- data/doc/AeEasy/Core/Plugin/InitializeHook.html +220 -0
- data/doc/AeEasy/Core/Plugin/Parser.html +259 -0
- data/doc/AeEasy/Core/Plugin/ParserBehavior.html +420 -0
- data/doc/AeEasy/Core/Plugin/Seeder.html +635 -0
- data/doc/AeEasy/Core/Plugin/SeederBehavior.html +282 -0
- data/doc/AeEasy/Core/SmartCollection.html +1386 -0
- data/doc/_index.html +329 -0
- data/doc/class_list.html +51 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +58 -0
- data/doc/css/style.css +496 -0
- data/doc/file.README.html +91 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +17 -0
- data/doc/index.html +91 -0
- data/doc/js/app.js +292 -0
- data/doc/js/full_list.js +216 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +811 -0
- data/doc/top-level-namespace.html +110 -0
- data/lib/ae_easy/core.rb +241 -0
- data/lib/ae_easy/core/config.rb +25 -0
- data/lib/ae_easy/core/exception.rb +8 -0
- data/lib/ae_easy/core/exception/outdated_error.rb +9 -0
- data/lib/ae_easy/core/helper.rb +8 -0
- data/lib/ae_easy/core/helper/cookie.rb +209 -0
- data/lib/ae_easy/core/mock.rb +44 -0
- data/lib/ae_easy/core/mock/fake_db.rb +280 -0
- data/lib/ae_easy/core/mock/fake_executor.rb +207 -0
- data/lib/ae_easy/core/mock/fake_parser.rb +31 -0
- data/lib/ae_easy/core/mock/fake_seeder.rb +28 -0
- data/lib/ae_easy/core/plugin.rb +15 -0
- data/lib/ae_easy/core/plugin/collection_vault.rb +23 -0
- data/lib/ae_easy/core/plugin/config_behavior.rb +43 -0
- data/lib/ae_easy/core/plugin/context_integrator.rb +60 -0
- data/lib/ae_easy/core/plugin/initialize_hook.rb +17 -0
- data/lib/ae_easy/core/plugin/parser.rb +19 -0
- data/lib/ae_easy/core/plugin/parser_behavior.rb +39 -0
- data/lib/ae_easy/core/plugin/seeder.rb +34 -0
- data/lib/ae_easy/core/plugin/seeder_behavior.rb +21 -0
- data/lib/ae_easy/core/smart_collection.rb +236 -0
- data/lib/ae_easy/core/version.rb +6 -0
- metadata +225 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.18
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
pathId = "";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
|
41
|
+
|
42
|
+
<span class="title">Top Level Namespace</span>
|
43
|
+
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="search">
|
47
|
+
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
49
|
+
href="class_list.html">
|
50
|
+
|
51
|
+
<svg width="24" height="24">
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
55
|
+
</svg>
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
</h1>
|
67
|
+
<div class="box_info">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<h2>Defined Under Namespace</h2>
|
82
|
+
<p class="children">
|
83
|
+
|
84
|
+
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="AeEasy.html" title="AeEasy (module)">AeEasy</a></span>
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
</p>
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<div id="footer">
|
103
|
+
Generated on Wed Feb 13 21:43:50 2019 by
|
104
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.18 (ruby-2.5.3).
|
106
|
+
</div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
</body>
|
110
|
+
</html>
|
data/lib/ae_easy/core.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'answersengine'
|
3
|
+
require 'ae_easy/core/smart_collection'
|
4
|
+
require 'ae_easy/core/exception'
|
5
|
+
require 'ae_easy/core/plugin'
|
6
|
+
require 'ae_easy/core/helper'
|
7
|
+
require 'ae_easy/core/config'
|
8
|
+
require 'ae_easy/core/mock'
|
9
|
+
require 'ae_easy/core/version'
|
10
|
+
|
11
|
+
module AeEasy
|
12
|
+
module Core
|
13
|
+
class << self
|
14
|
+
# Get AeEasy-core gem root directory path.
|
15
|
+
# @private
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
def gem_root
|
19
|
+
File.expand_path File.join(File.dirname(__FILE__), '../..')
|
20
|
+
end
|
21
|
+
|
22
|
+
# Execute an action for all scripts within a directory.
|
23
|
+
#
|
24
|
+
# @param [String] dir Directory containing `.rb` scripts.
|
25
|
+
# @param [Hash] opts ({}) Configuration options.
|
26
|
+
# @option opts [Array] :except (nil) Literal file collection excluded from process.
|
27
|
+
#
|
28
|
+
# @yieldparam [String] path Script file path.
|
29
|
+
def all_scripts dir, opts = {}, &block
|
30
|
+
excluded_files = (opts[:except] || []).map{|f|File.expand_path File.join(dir, f)}
|
31
|
+
files = Dir[File.join(File.expand_path(dir), '*.rb')] - excluded_files
|
32
|
+
block ||= proc{}
|
33
|
+
files.sort.each &block
|
34
|
+
end
|
35
|
+
|
36
|
+
# Require all scripts within a directory.
|
37
|
+
#
|
38
|
+
# @param [String] dir Directory containing `.rb` scripts.
|
39
|
+
# @param [Hash] opts ({}) Configuration options.
|
40
|
+
# @option opts [Array] :except (nil) Literal file collection excluded from process.
|
41
|
+
def require_all dir, opts = {}
|
42
|
+
real_dir = options = nil
|
43
|
+
real_except = []
|
44
|
+
$LOAD_PATH.each do |load_path|
|
45
|
+
real_dir = File.join load_path, dir
|
46
|
+
next unless File.directory? real_dir
|
47
|
+
real_except = (opts[:except] || []).map{|f| "#{f}.rb"}
|
48
|
+
options = opts.merge except: real_except
|
49
|
+
all_scripts(real_dir, options) {|file| require file}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Require all relative scripts paths within a directory.
|
54
|
+
#
|
55
|
+
# @param [String] dir Directory containing `.rb` scripts.
|
56
|
+
# @param [Hash] opts ({}) Configuration options.
|
57
|
+
# @option opts [Array] :except (nil) Literal file collection excluded from process.
|
58
|
+
def require_relative_all dir, opts = {}
|
59
|
+
real_except = (opts[:except] || []).map{|f| "#{f}.rb"}
|
60
|
+
options = opts.merge except: real_except
|
61
|
+
all_scripts(dir, options) {|file| require file}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Expose an environment into an object instance as methods.
|
65
|
+
#
|
66
|
+
# @param object Object instance to expose env into.
|
67
|
+
# @param [Array] env Hash with methods name as keys and blocks as actions.
|
68
|
+
#
|
69
|
+
# @return `object`
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# class Foo
|
73
|
+
# def hello_person
|
74
|
+
# 'Hello person!'
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# env = {
|
79
|
+
# hello_world: lambda{return 'Hello world!'},
|
80
|
+
# hello_sky: proc{return 'Hello sky!'}
|
81
|
+
# }
|
82
|
+
# my_object = Foo.new
|
83
|
+
# AeEasy::Core.expose_to my_object, env
|
84
|
+
#
|
85
|
+
# puts my_object.hello_world
|
86
|
+
# # => 'Hello world!'
|
87
|
+
# puts my_object.hello_sky
|
88
|
+
# # => 'Hello sky!'
|
89
|
+
# puts my_object.hello_person
|
90
|
+
# # => 'Hello person!'
|
91
|
+
def expose_to object, env
|
92
|
+
metaclass = class << object; self; end
|
93
|
+
env.each do |key, block|
|
94
|
+
metaclass.send(:define_method, key, block)
|
95
|
+
end
|
96
|
+
object
|
97
|
+
end
|
98
|
+
|
99
|
+
# Retrieve instance methods from an object.
|
100
|
+
#
|
101
|
+
# @param object Object with instance methods.
|
102
|
+
#
|
103
|
+
# @return [Array]
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# class Foo
|
107
|
+
# def hello_world
|
108
|
+
# 'Hello world!'
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# def hello_person
|
112
|
+
# 'Hello person!'
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# my_object = Foo.new
|
117
|
+
# AeEasy::Core.instance_methods_from my_object
|
118
|
+
# # => [:hello_world, :hello_person]
|
119
|
+
def instance_methods_from object
|
120
|
+
object.methods(false) - Object.new.methods(false)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Mock instances methods from the source into target object.
|
124
|
+
#
|
125
|
+
# @param source Object with instance methods to mock.
|
126
|
+
# @param target Object instance to mock methods into.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# class Boo
|
130
|
+
# attr_accessor :message
|
131
|
+
# def initialize
|
132
|
+
# message = 'Hello world!'
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# def hello_world
|
136
|
+
# message
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# class Foo
|
141
|
+
# def hello_person
|
142
|
+
# 'Hello person!'
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# source = Boo.new
|
147
|
+
# target = Foo.new
|
148
|
+
# AeEasy::Core.mock_instance_methods source target
|
149
|
+
#
|
150
|
+
# puts target.hello_world
|
151
|
+
# # => 'Hello world!'
|
152
|
+
# puts target.hello_person
|
153
|
+
# # => 'Hello person!'
|
154
|
+
#
|
155
|
+
# source.message = 'Hello world again!'
|
156
|
+
# puts target.hello_world
|
157
|
+
# # => 'Hello world again!'
|
158
|
+
def mock_instance_methods source, target
|
159
|
+
# Get instance unique methods
|
160
|
+
method_list = instance_methods_from source
|
161
|
+
method_list.delete :context_binding if method_list.include? :context_binding
|
162
|
+
|
163
|
+
# Build env reflecting source unique methods
|
164
|
+
env = {}
|
165
|
+
method_list.each do |method|
|
166
|
+
env[method] = lambda{|*args|source.send(method, *args)}
|
167
|
+
end
|
168
|
+
|
169
|
+
# Mock source unique methods into target
|
170
|
+
expose_to target, env
|
171
|
+
end
|
172
|
+
|
173
|
+
# Generate a compatibility report from a source and a fragment as a hash.
|
174
|
+
#
|
175
|
+
# @param [Array] source Item collection to represent the universe.
|
176
|
+
# @param [Array] fragment Item collection to compare againt +source+.
|
177
|
+
#
|
178
|
+
# @return [Hash]
|
179
|
+
# * `:missing [Array]` (`[]`) Methods on `fragment` only.
|
180
|
+
# * `:new [Array]` (`[]`) Methods on `source` only.
|
181
|
+
# * `:is_compatible [Boolean]` true when all `fragment`'s methods are present on `source`.
|
182
|
+
#
|
183
|
+
# @example Analyze when uncompatible `fragment` because of `source` missing fields.
|
184
|
+
# AeEasy::Core.analyze_compatibility [1,2,3,4,5], [1,2,6]
|
185
|
+
# # => {missing: [6], new: [3,4,5], is_compatible: false}
|
186
|
+
#
|
187
|
+
# @example Analyze when compatible.
|
188
|
+
# AeEasy::Core.analyze_compatibility [1,2,3,4,5], [1,2,3]
|
189
|
+
# # => {missing: [], new: [4,5], is_compatible: true}
|
190
|
+
def analyze_compatibility source, fragment
|
191
|
+
intersection = source & fragment
|
192
|
+
{
|
193
|
+
missing: fragment - intersection,
|
194
|
+
new: source - intersection,
|
195
|
+
is_compatible: (intersection.count == fragment.count)
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
# Deep stringify keys from a hash.
|
200
|
+
#
|
201
|
+
# @param [Hash] hash Source hash to stringify keys.
|
202
|
+
# @param [Boolean] should_clone (true) Target a hash clone to avoid affecting the same hash object.
|
203
|
+
#
|
204
|
+
# @return [Hash]
|
205
|
+
def deep_stringify_keys hash, should_clone = true
|
206
|
+
pair_collection = hash.map{|k,v| [k.to_s,v]}
|
207
|
+
target = should_clone ? {} : hash
|
208
|
+
target.clear
|
209
|
+
pair_collection.each do |pair|
|
210
|
+
key, value = pair
|
211
|
+
target[key] = value.is_a?(Hash) ? deep_stringify_keys(value, should_clone) : value
|
212
|
+
end
|
213
|
+
target
|
214
|
+
end
|
215
|
+
|
216
|
+
# Deep stringify all keys on hash object.
|
217
|
+
#
|
218
|
+
# @param [Hash] hash Hash to stringify keys.
|
219
|
+
#
|
220
|
+
# @return [Hash]
|
221
|
+
def deep_stringify_keys! hash
|
222
|
+
deep_stringify_keys hash, false
|
223
|
+
end
|
224
|
+
|
225
|
+
# Deep clone a hash while keeping it's values object references.
|
226
|
+
#
|
227
|
+
# @param [Hash] hash Hash to clone.
|
228
|
+
# @param [Boolean] should_clone (false) Clone values when true.
|
229
|
+
#
|
230
|
+
# @return [Hash] Hash clone.
|
231
|
+
def deep_clone hash, should_clone = false
|
232
|
+
target = {}
|
233
|
+
hash.each do |key, value|
|
234
|
+
value = value.is_a?(Hash) ? deep_clone(value, should_clone) : (should_clone ? value.clone : value)
|
235
|
+
target[key] = value
|
236
|
+
end
|
237
|
+
target
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AeEasy
|
2
|
+
module Core
|
3
|
+
# Configuration manager tool useful for global configuration data accross
|
4
|
+
# the scraping process.
|
5
|
+
class Config
|
6
|
+
include AeEasy::Core::Plugin::InitializeHook
|
7
|
+
include AeEasy::Core::Plugin::ConfigBehavior
|
8
|
+
|
9
|
+
alias :collection_key :config_collection_key
|
10
|
+
alias :collection :config_collection
|
11
|
+
|
12
|
+
# Initialize config object
|
13
|
+
#
|
14
|
+
# @param [Hash] opts ({}) Configuration options.
|
15
|
+
#
|
16
|
+
# @see AeEasy::Core::Plugin::ConfigBehavior#initialize_hook_core_config_behavior
|
17
|
+
def initialize opts = {}
|
18
|
+
opts = opts.merge(
|
19
|
+
config_collection: opts[:collection]
|
20
|
+
)
|
21
|
+
initialize_hooks opts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module AeEasy
|
2
|
+
module Core
|
3
|
+
module Helper
|
4
|
+
# Helper used for lower level cookie management.
|
5
|
+
class Cookie
|
6
|
+
class << self
|
7
|
+
# Parse request cookies on different formats.
|
8
|
+
#
|
9
|
+
# @param [String,Hash,Array] cookies Cookies to parse.
|
10
|
+
# @param [Hash] cookie_hash ({}) External hash to store parsed cookies.
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
# @example Parse from string.
|
15
|
+
# parse_from_request 'aaa=111; bbb=222'
|
16
|
+
# # => {'aaa' => 111, 'bbb' => 222}
|
17
|
+
#
|
18
|
+
# @example Parse from array.
|
19
|
+
# cookies = [
|
20
|
+
# 'aaa=111',
|
21
|
+
# 'bbb=222'
|
22
|
+
# ]
|
23
|
+
# parse_from_response cookies
|
24
|
+
# # => {'aaa' => 111, 'bbb' => 222}
|
25
|
+
#
|
26
|
+
# @example Parse with `cookie_hash`.
|
27
|
+
# cookie_hash = {'ccc' => 333}
|
28
|
+
# parse_from_request 'aaa=111; bbb=222', cookie_hash
|
29
|
+
# cookie_hash
|
30
|
+
# # => {'aaa' => 1, 'bbb' => 2, 'ccc' => 333}
|
31
|
+
def parse_from_request cookies, cookie_hash = {}
|
32
|
+
# Retrieve from hash
|
33
|
+
if cookies.is_a? Hash
|
34
|
+
cookie_hash.merge! cookies
|
35
|
+
return cookie_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extract from string
|
39
|
+
cookies = cookies.split '; ' if cookies.is_a? String
|
40
|
+
|
41
|
+
# Extract from array
|
42
|
+
cookies&.each do |raw_cookie|
|
43
|
+
key, value = raw_cookie.split('=', 2)
|
44
|
+
cookie_hash[key] = value
|
45
|
+
end
|
46
|
+
cookie_hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# Parse response cookies on different formats.
|
50
|
+
#
|
51
|
+
# @param [String,Hash,Array] cookies Cookies to parse.
|
52
|
+
# @param [Hash] cookie_hash ({}) External hash to store parsed cookies.
|
53
|
+
#
|
54
|
+
# @return [Hash]
|
55
|
+
#
|
56
|
+
# @example Parse from string
|
57
|
+
# parse_from_response 'aaa=111; bbb=222'
|
58
|
+
# # => {'aaa' => 111, 'bbb' => 222}
|
59
|
+
#
|
60
|
+
# @example Parse from array.
|
61
|
+
# cookies = [
|
62
|
+
# 'aaa=111; Expires=Thu, Jan 01 1970 00:00:00 UTC; path=/',
|
63
|
+
# 'bbb=222; path=/',
|
64
|
+
# 'ccc=333; path=/; expires=Wed, Jan 01 3000 00:00:00 UTC'
|
65
|
+
# ]
|
66
|
+
# parse_from_response cookies
|
67
|
+
# # => {'bbb' => 222, 'ccc' => 333}
|
68
|
+
#
|
69
|
+
# @example Parse with `cookie_hash`.
|
70
|
+
# cookie_hash = {'ccc' => 333}
|
71
|
+
# parse_from_response 'aaa=111; bbb=222', cookie_hash
|
72
|
+
# cookie_hash
|
73
|
+
# # => {'aaa' => 111, 'bbb' => 222, 'ccc' => 333}
|
74
|
+
def parse_from_response cookies, cookie_hash = {}
|
75
|
+
# Retrieve from hash
|
76
|
+
if cookies.is_a? Hash
|
77
|
+
cookie_hash.merge! cookies
|
78
|
+
return cookie_hash
|
79
|
+
end
|
80
|
+
# Retrieve from String
|
81
|
+
cookies = cookies.split '; ' if cookies.is_a? String
|
82
|
+
|
83
|
+
# Extract from array
|
84
|
+
info = cookie = expires = key = value = nil
|
85
|
+
cookies&.each do |raw_cookie|
|
86
|
+
# Extract cookie data
|
87
|
+
key_pair = raw_cookie.scan(/(?:;\s+([^\=]+)=([^;]*))/i) || []
|
88
|
+
cookie = key_pair.inject(Hash.new){|h,i|h[i[0].to_s.downcase] = i[1]; h}
|
89
|
+
cookie[:key], cookie[:value] = raw_cookie.match(/^\s*(?<key>[^\=]+)\=(?<value>[^;]*)/i)&.captures
|
90
|
+
|
91
|
+
# Check cookie expire
|
92
|
+
expires = cookie['expires'].nil? ? nil : Time.parse(cookie['expires'])
|
93
|
+
if !expires.nil? && Time.now > expires
|
94
|
+
cookie_hash.delete cookie[:key]
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
# Save cookie
|
99
|
+
cookie_hash[cookie[:key]] = cookie[:value]
|
100
|
+
end
|
101
|
+
cookie_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
# Apply request and response cookies as a hash.
|
105
|
+
#
|
106
|
+
# @param [String,Array,Hash] request_cookies Cookies to parse.
|
107
|
+
# @param [String,Array,Hash] response_cookies Cookies to parse.
|
108
|
+
#
|
109
|
+
# @return [Hash]
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# request_cookies = 'aaa=111; ddd=444'
|
113
|
+
# response_cookies = [
|
114
|
+
# 'aaa=111; Expires=Thu, Jan 01 1970 00:00:00 UTC; path=/',
|
115
|
+
# 'bbb=222; path=/',
|
116
|
+
# 'ccc=333; path=/; expires=Wed, Jan 01 3000 00:00:00 UTC'
|
117
|
+
# ]
|
118
|
+
# update_as_hash , response_cookies
|
119
|
+
# # => {'bbb' => 222, 'ccc' => 333, 'ddd' => 444}
|
120
|
+
def update_as_hash request_cookies, response_cookies
|
121
|
+
cookie_hash = {}
|
122
|
+
parse_from_request request_cookies, cookie_hash
|
123
|
+
parse_from_response response_cookies, cookie_hash
|
124
|
+
cookie_hash
|
125
|
+
end
|
126
|
+
|
127
|
+
# Encode cookies as request cookie string.
|
128
|
+
#
|
129
|
+
# @param [Hash] cookie_hash Hash with cookies.
|
130
|
+
#
|
131
|
+
# @return [String]
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
# cookie_hash = {
|
135
|
+
# 'aaa' => 111,
|
136
|
+
# 'bbb' => 222
|
137
|
+
# }
|
138
|
+
# encode_to_header cookie_hash
|
139
|
+
# # => 'aaa=111; bbb=222'
|
140
|
+
def encode_to_header cookie_hash
|
141
|
+
cookie_hash.map{|k,v| "#{k}=#{v}"}.join '; '
|
142
|
+
end
|
143
|
+
|
144
|
+
# Apply request and response cookies as a string with request format.
|
145
|
+
#
|
146
|
+
# @param [String,Array,Hash] request_cookies Cookies to parse.
|
147
|
+
# @param [String,Array,Hash] response_cookies Cookies to parse.
|
148
|
+
#
|
149
|
+
# @return [String]
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# request_cookies = 'aaa=111; ddd=444'
|
153
|
+
# response_cookies = [
|
154
|
+
# 'aaa=111; Expires=Thu, Jan 01 1970 00:00:00 UTC; path=/',
|
155
|
+
# 'bbb=222; path=/',
|
156
|
+
# 'ccc=333; path=/; expires=Wed, Jan 01 3000 00:00:00 UTC'
|
157
|
+
# ]
|
158
|
+
# update_as_hash , response_cookies
|
159
|
+
# # => 'bbb=222; ccc=333; ddd=444'
|
160
|
+
def update request_cookies, response_cookies
|
161
|
+
cookie_hash = update_as_hash request_cookies, response_cookies
|
162
|
+
encode_to_header cookie_hash
|
163
|
+
end
|
164
|
+
|
165
|
+
# Compare if cookie is included into base cookie
|
166
|
+
#
|
167
|
+
# @param [Hash] base_cookie_hash Hash that represent universe.
|
168
|
+
# @param [Hash] cookie_hash Hash that represents to compare.
|
169
|
+
#
|
170
|
+
# @return [Boolean]
|
171
|
+
#
|
172
|
+
# @example Check a success match.
|
173
|
+
# base_cookie_hash = {
|
174
|
+
# 'aaa' => 111,
|
175
|
+
# 'bbb' => 222,
|
176
|
+
# 'ccc' => 333,
|
177
|
+
# 'ddd' => 444
|
178
|
+
# }
|
179
|
+
# cookie_hash = {
|
180
|
+
# 'bbb' => 222,
|
181
|
+
# 'ddd' => 444
|
182
|
+
# }
|
183
|
+
# include? base_cookie_hash, cookie_hash
|
184
|
+
# # => true
|
185
|
+
#
|
186
|
+
# @example Check with fail match.
|
187
|
+
# base_cookie_hash = {
|
188
|
+
# 'aaa' => 111,
|
189
|
+
# 'bbb' => 222,
|
190
|
+
# 'ccc' => 333,
|
191
|
+
# 'ddd' => 444
|
192
|
+
# }
|
193
|
+
# cookie_hash = {
|
194
|
+
# 'bbb' => 555,
|
195
|
+
# 'ddd' => 444
|
196
|
+
# }
|
197
|
+
# include? base_cookie_hash, cookie_hash
|
198
|
+
# # => false
|
199
|
+
def include? base_cookie_hash, cookie_hash
|
200
|
+
cookie_hash.each do |key, value|
|
201
|
+
return false unless base_cookie_hash.has_key?(key) && base_cookie_hash[key] == value
|
202
|
+
end
|
203
|
+
true
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|