ae_easy-core 0.2.0 → 0.2.1
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 +4 -4
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +8 -4
- data/Rakefile +0 -10
- data/ae_easy-core.gemspec +6 -13
- data/lib/ae_easy/core.rb +4 -256
- metadata +18 -125
- data/doc/AeEasy.html +0 -117
- data/doc/AeEasy/Core.html +0 -1590
- data/doc/AeEasy/Core/Config.html +0 -311
- data/doc/AeEasy/Core/Exception.html +0 -117
- data/doc/AeEasy/Core/Exception/OutdatedError.html +0 -135
- data/doc/AeEasy/Core/Helper.html +0 -117
- data/doc/AeEasy/Core/Helper/Cookie.html +0 -1070
- data/doc/AeEasy/Core/Mock.html +0 -282
- data/doc/AeEasy/Core/Mock/FakeDb.html +0 -3779
- data/doc/AeEasy/Core/Mock/FakeExecutor.html +0 -3289
- data/doc/AeEasy/Core/Mock/FakeFinisher.html +0 -160
- data/doc/AeEasy/Core/Mock/FakeParser.html +0 -160
- data/doc/AeEasy/Core/Mock/FakeSeeder.html +0 -160
- data/doc/AeEasy/Core/Plugin.html +0 -117
- data/doc/AeEasy/Core/Plugin/CollectionVault.html +0 -299
- data/doc/AeEasy/Core/Plugin/ConfigBehavior.html +0 -541
- data/doc/AeEasy/Core/Plugin/ContextIntegrator.html +0 -445
- data/doc/AeEasy/Core/Plugin/Executor.html +0 -259
- data/doc/AeEasy/Core/Plugin/ExecutorBehavior.html +0 -344
- data/doc/AeEasy/Core/Plugin/Finisher.html +0 -265
- data/doc/AeEasy/Core/Plugin/FinisherBehavior.html +0 -142
- data/doc/AeEasy/Core/Plugin/InitializeHook.html +0 -220
- data/doc/AeEasy/Core/Plugin/Parser.html +0 -270
- data/doc/AeEasy/Core/Plugin/ParserBehavior.html +0 -235
- data/doc/AeEasy/Core/Plugin/Seeder.html +0 -674
- data/doc/AeEasy/Core/Plugin/SeederBehavior.html +0 -142
- data/doc/AeEasy/Core/SmartCollection.html +0 -1087
- data/doc/_index.html +0 -364
- data/doc/class_list.html +0 -51
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -58
- data/doc/css/style.css +0 -496
- data/doc/file.README.html +0 -91
- data/doc/file_list.html +0 -56
- data/doc/frames.html +0 -17
- data/doc/index.html +0 -91
- data/doc/js/app.js +0 -303
- data/doc/js/full_list.js +0 -216
- data/doc/js/jquery.js +0 -4
- data/doc/method_list.html +0 -939
- data/doc/top-level-namespace.html +0 -110
- data/lib/ae_easy/core/config.rb +0 -27
- data/lib/ae_easy/core/exception.rb +0 -8
- data/lib/ae_easy/core/exception/outdated_error.rb +0 -9
- data/lib/ae_easy/core/helper.rb +0 -8
- data/lib/ae_easy/core/helper/cookie.rb +0 -209
- data/lib/ae_easy/core/mock.rb +0 -45
- data/lib/ae_easy/core/mock/fake_db.rb +0 -561
- data/lib/ae_easy/core/mock/fake_executor.rb +0 -373
- data/lib/ae_easy/core/mock/fake_finisher.rb +0 -28
- data/lib/ae_easy/core/mock/fake_parser.rb +0 -33
- data/lib/ae_easy/core/mock/fake_seeder.rb +0 -28
- data/lib/ae_easy/core/plugin.rb +0 -19
- data/lib/ae_easy/core/plugin/collection_vault.rb +0 -23
- data/lib/ae_easy/core/plugin/config_behavior.rb +0 -43
- data/lib/ae_easy/core/plugin/context_integrator.rb +0 -60
- data/lib/ae_easy/core/plugin/executor.rb +0 -19
- data/lib/ae_easy/core/plugin/executor_behavior.rb +0 -32
- data/lib/ae_easy/core/plugin/finisher.rb +0 -19
- data/lib/ae_easy/core/plugin/finisher_behavior.rb +0 -9
- data/lib/ae_easy/core/plugin/initialize_hook.rb +0 -17
- data/lib/ae_easy/core/plugin/parser.rb +0 -19
- data/lib/ae_easy/core/plugin/parser_behavior.rb +0 -17
- data/lib/ae_easy/core/plugin/seeder.rb +0 -44
- data/lib/ae_easy/core/plugin/seeder_behavior.rb +0 -9
- data/lib/ae_easy/core/smart_collection.rb +0 -236
- data/lib/ae_easy/core/version.rb +0 -6
data/lib/ae_easy/core/plugin.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'ae_easy/core/plugin/initialize_hook'
|
2
|
-
require 'ae_easy/core/plugin/context_integrator'
|
3
|
-
require 'ae_easy/core/plugin/collection_vault'
|
4
|
-
require 'ae_easy/core/plugin/config_behavior'
|
5
|
-
require 'ae_easy/core/plugin/executor_behavior'
|
6
|
-
require 'ae_easy/core/plugin/executor'
|
7
|
-
require 'ae_easy/core/plugin/parser_behavior'
|
8
|
-
require 'ae_easy/core/plugin/parser'
|
9
|
-
require 'ae_easy/core/plugin/seeder_behavior'
|
10
|
-
require 'ae_easy/core/plugin/seeder'
|
11
|
-
require 'ae_easy/core/plugin/finisher_behavior'
|
12
|
-
require 'ae_easy/core/plugin/finisher'
|
13
|
-
|
14
|
-
module AeEasy
|
15
|
-
module Core
|
16
|
-
module Plugin
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module CollectionVault
|
5
|
-
# Stored collections info as hash.
|
6
|
-
def collections
|
7
|
-
@collections ||= {}
|
8
|
-
end
|
9
|
-
|
10
|
-
# Add a new collection
|
11
|
-
#
|
12
|
-
# @param [Symbol] key Collection key used to lookup for collection name.
|
13
|
-
# @param [String] name Collection name used on outputs.
|
14
|
-
def add_collection key, name
|
15
|
-
if collections.has_key? key
|
16
|
-
raise "Can't add \"#{key}\" collection, it already exists!"
|
17
|
-
end
|
18
|
-
collections[key] = name
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module ConfigBehavior
|
5
|
-
include AeEasy::Core::Plugin::ContextIntegrator
|
6
|
-
include AeEasy::Core::Plugin::CollectionVault
|
7
|
-
|
8
|
-
attr_reader :config_collection_key
|
9
|
-
|
10
|
-
# Hook to map config behavior on self
|
11
|
-
#
|
12
|
-
# @param [Hash] opts ({}) Configuration options.
|
13
|
-
# @option opts [Array] :config_collection ([:config, 'config']) Key value pair array to se a custom collection.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# initialize_hook_core_config_behavior config_collection: [:my_config, 'abc']
|
17
|
-
# config_collection
|
18
|
-
# # => 'abc'
|
19
|
-
def initialize_hook_core_config_behavior opts = {}
|
20
|
-
@config_collection_key, collection = opts[:config_collection] || [:config, 'config']
|
21
|
-
add_collection config_collection_key, collection
|
22
|
-
end
|
23
|
-
|
24
|
-
# Get config collection name.
|
25
|
-
# @return [String]
|
26
|
-
def config_collection
|
27
|
-
collections[config_collection_key]
|
28
|
-
end
|
29
|
-
|
30
|
-
# Find a configuration value by item key.
|
31
|
-
#
|
32
|
-
# @param [Symbol] key Item key to find.
|
33
|
-
#
|
34
|
-
# @note Instance must implement:
|
35
|
-
# * `find_output(collection, query)`
|
36
|
-
def find_config key
|
37
|
-
value = find_output config_collection, '_id' => key
|
38
|
-
value ||= {'_collection' => config_collection, '_id' => key}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module ContextIntegrator
|
5
|
-
# Last mocked ontext object.
|
6
|
-
attr_reader :context
|
7
|
-
|
8
|
-
# Mock a context methods into self.
|
9
|
-
#
|
10
|
-
# @param origin Object that represents the context to mock.
|
11
|
-
#
|
12
|
-
# @example
|
13
|
-
# class MyContext
|
14
|
-
# attr_accessor :message
|
15
|
-
# def initialize
|
16
|
-
# message = 'Hello world!'
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# def hello_world
|
20
|
-
# message
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# class Foo
|
25
|
-
# include ContextIntegrator
|
26
|
-
#
|
27
|
-
# def hello_person
|
28
|
-
# 'Hello person!'
|
29
|
-
# end
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# context = MyContext.new
|
33
|
-
# my_object = Foo.new
|
34
|
-
# my_object.mock_context context
|
35
|
-
#
|
36
|
-
# puts my_object.hello_world
|
37
|
-
# # => 'Hello world!'
|
38
|
-
# puts my_object.hello_person
|
39
|
-
# # => 'Hello person!'
|
40
|
-
#
|
41
|
-
# context.message = 'Hello world again!'
|
42
|
-
# puts my_object.hello_world
|
43
|
-
# # => 'Hello world again!
|
44
|
-
def mock_context origin
|
45
|
-
@context = origin
|
46
|
-
AeEasy::Core.mock_instance_methods context, self
|
47
|
-
end
|
48
|
-
|
49
|
-
# Hook to mock context on initialize.
|
50
|
-
#
|
51
|
-
# @param [Hash] opts ({}) Configuration options.
|
52
|
-
# @option opts :context Object that represents the context to mock.
|
53
|
-
def initialize_hook_core_context_integrator opts = [{}]
|
54
|
-
raise ':context object is required.' if opts[:context].nil?
|
55
|
-
mock_context opts[:context]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module Executor
|
5
|
-
include AeEasy::Core::Plugin::InitializeHook
|
6
|
-
include AeEasy::Core::Plugin::ExecutorBehavior
|
7
|
-
|
8
|
-
# Initialize hooks.
|
9
|
-
#
|
10
|
-
# @param [Hash] opts ({}) Configuration options.
|
11
|
-
#
|
12
|
-
# @see AeEasy::Core::Plugin::ContextIntegrator#initialize_hook_core_context_integrator
|
13
|
-
def initialize opts = {}
|
14
|
-
initialize_hooks opts
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module ExecutorBehavior
|
5
|
-
include AeEasy::Core::Plugin::ContextIntegrator
|
6
|
-
|
7
|
-
# Enqueue a single/multiple pages for fetch. Analog to `save_pages`.
|
8
|
-
#
|
9
|
-
# @param [Array,Hash] object Pages to save being Hash when single and
|
10
|
-
# Array when many.
|
11
|
-
#
|
12
|
-
# @note Instance must implement:
|
13
|
-
# * `save_pages(pages)`
|
14
|
-
def enqueue object
|
15
|
-
object = [object] unless object.is_a? Array
|
16
|
-
save_pages object
|
17
|
-
end
|
18
|
-
|
19
|
-
# Save a single/multiple outputs. Analog to `save_outputs`.
|
20
|
-
#
|
21
|
-
# @param [Array,Hash] object Outputs to save being Hash when single and Array when many.
|
22
|
-
#
|
23
|
-
# @note Instance must implement:
|
24
|
-
# * `save_outputs(outputs)`
|
25
|
-
def save object
|
26
|
-
object = [object] unless object.is_a? Array
|
27
|
-
save_outputs object
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module Finisher
|
5
|
-
include AeEasy::Core::Plugin::InitializeHook
|
6
|
-
include AeEasy::Core::Plugin::FinisherBehavior
|
7
|
-
|
8
|
-
# Initialize finisher and hooks.
|
9
|
-
#
|
10
|
-
# @param [Hash] opts ({}) Configuration options.
|
11
|
-
#
|
12
|
-
# @see AeEasy::Core::Plugin::ContextIntegrator#initialize_hook_core_context_integrator
|
13
|
-
def initialize opts = {}
|
14
|
-
initialize_hooks opts
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module InitializeHook
|
5
|
-
# Execute all methods with `initilaize_hook_` prefix (hooks).
|
6
|
-
#
|
7
|
-
# @param [Hash] opts ({}) Configuration options sent to all hooks.
|
8
|
-
def initialize_hooks opts = {}
|
9
|
-
initializers = self.methods.select{|i|i.to_s =~ /^initialize_hook_/}
|
10
|
-
initializers.each do |method|
|
11
|
-
self.send method, opts
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module Parser
|
5
|
-
include AeEasy::Core::Plugin::InitializeHook
|
6
|
-
include AeEasy::Core::Plugin::ParserBehavior
|
7
|
-
|
8
|
-
# Initialize parser and hooks.
|
9
|
-
#
|
10
|
-
# @param [Hash] opts ({}) Configuration options.
|
11
|
-
#
|
12
|
-
# @see AeEasy::Core::Plugin::ContextIntegrator#initialize_hook_core_context_integrator
|
13
|
-
def initialize opts = {}
|
14
|
-
initialize_hooks opts
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module ParserBehavior
|
5
|
-
include AeEasy::Core::Plugin::ExecutorBehavior
|
6
|
-
|
7
|
-
# Alias to `page['vars']`.
|
8
|
-
#
|
9
|
-
# @note Instance must implement:
|
10
|
-
# * `page`
|
11
|
-
def vars
|
12
|
-
page['vars']
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
module Plugin
|
4
|
-
module Seeder
|
5
|
-
include AeEasy::Core::Plugin::InitializeHook
|
6
|
-
include AeEasy::Core::Plugin::SeederBehavior
|
7
|
-
|
8
|
-
# Root input directory path.
|
9
|
-
# @return [String]
|
10
|
-
attr_accessor :root_input_dir
|
11
|
-
|
12
|
-
# Referer to use on page seeding.
|
13
|
-
# @return [String]
|
14
|
-
attr_accessor :referer
|
15
|
-
|
16
|
-
# Cookie to use on page seeing.
|
17
|
-
# @return [String]
|
18
|
-
attr_accessor :cookie
|
19
|
-
|
20
|
-
# Hook to initialize seeder object.
|
21
|
-
#
|
22
|
-
# @param [Hash] opts ({}) Configuration options.
|
23
|
-
# @option opts [String] :root_input_dir (nil) Root directory for inputs.
|
24
|
-
# @option opts [String] :referer (nil) New pages referer, useful to dynamic setups.
|
25
|
-
# @option opts [String] :cookie (nil) Cookie to use on seeded pages fetchs.
|
26
|
-
def initialize_hook_core_seeder opts = {}
|
27
|
-
@root_input_dir = opts[:root_input_dir]
|
28
|
-
@referer = opts[:referer]
|
29
|
-
@cookie = opts[:cookie]
|
30
|
-
end
|
31
|
-
|
32
|
-
# Initialize seeder and hooks.
|
33
|
-
#
|
34
|
-
# @param [Hash] opts ({}) Configuration options.
|
35
|
-
#
|
36
|
-
# @see AeEasy::Core::Plugin::ContextIntegrator#initialize_hook_core_context_integrator
|
37
|
-
# @see #initialize_hook_core_seeder
|
38
|
-
def initialize opts = {}
|
39
|
-
initialize_hooks opts
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,236 +0,0 @@
|
|
1
|
-
module AeEasy
|
2
|
-
module Core
|
3
|
-
# Smart collection capable to avoid duplicates on insert by matching id
|
4
|
-
# defined fields along events.
|
5
|
-
class SmartCollection < Array
|
6
|
-
# Implemented event list.
|
7
|
-
EVENTS = [
|
8
|
-
:before_defaults,
|
9
|
-
:before_match,
|
10
|
-
:before_insert,
|
11
|
-
:after_insert
|
12
|
-
]
|
13
|
-
|
14
|
-
# Key fields, analog to primary keys.
|
15
|
-
attr_reader :key_fields
|
16
|
-
# Default fields values. Apply to missing fields and null values.
|
17
|
-
attr_reader :defaults
|
18
|
-
|
19
|
-
# Initialize collection
|
20
|
-
#
|
21
|
-
# @param [Array] key_fields Key fields, analog to primary keys.
|
22
|
-
# @param [Hash] opts ({}) Configuration options.
|
23
|
-
# @option opts [Array] :values ([]) Initial values; will avoid duplicates on insert.
|
24
|
-
# @option opts [Hash] :defaults ({}) Default values. `proc` values will be executed to get default value.
|
25
|
-
#
|
26
|
-
# @example With default values.
|
27
|
-
# count = 0
|
28
|
-
# defaults = {
|
29
|
-
# 'id' => lambda{|item| count += 1},
|
30
|
-
# 'aaa' => 111,
|
31
|
-
# 'bbb' => proc{|item| item['ccc'].nil? ? 'No ccc' : 'Has ccc'}
|
32
|
-
# }
|
33
|
-
# values = [
|
34
|
-
# {'aaa' => 'Defaults apply on nil values only', 'bbb' => nil},
|
35
|
-
# {'ccc' => 'ddd'},
|
36
|
-
# {'id' => 'abc123'}
|
37
|
-
# ]
|
38
|
-
# new_item = {'bbb' => 'Look mom! no ccc'}
|
39
|
-
# collection = SmartCollection.new ['id'], defaults: defaults
|
40
|
-
# collection << new_item
|
41
|
-
# collection
|
42
|
-
# # => [
|
43
|
-
# # {'id' => 1, 'aaa' => 'Defaults apply on nil values only', 'bbb' => 'No ccc'},
|
44
|
-
# # {'id' => 2, 'aaa' => 111, 'bbb' => 'Has ccc', 'ccc' => 'ddd'},
|
45
|
-
# # {'id' => 'abc123', 'aaa' => 111, 'bbb' => 'No ccc'},
|
46
|
-
# # {'id' => 3, 'aaa' => 111, 'bbb' => 'Look mom! no ccc'}
|
47
|
-
# # ]
|
48
|
-
#
|
49
|
-
# @note Defaults will apply to missing fields and null values only.
|
50
|
-
def initialize key_fields, opts = {}
|
51
|
-
@key_fields = key_fields || []
|
52
|
-
@defaults = opts[:defaults] || {}
|
53
|
-
super 0
|
54
|
-
(opts[:values] || []).each{|item| self << item}
|
55
|
-
end
|
56
|
-
|
57
|
-
# Asigned events.
|
58
|
-
# @private
|
59
|
-
def events
|
60
|
-
@events ||= {}
|
61
|
-
end
|
62
|
-
|
63
|
-
# Add event binding by key and block.
|
64
|
-
#
|
65
|
-
# @param [Symbol] key Event name.
|
66
|
-
#
|
67
|
-
# @raise [ArgumentError] When unknown event key.
|
68
|
-
#
|
69
|
-
# @example before_defaults
|
70
|
-
# defaults = {'aaa' => 111}
|
71
|
-
# collection = SmartCollection.new [],
|
72
|
-
# defaults: defaults
|
73
|
-
# collection.bind_event(:before_defaults) do |collection, item|
|
74
|
-
# puts collection
|
75
|
-
# # => []
|
76
|
-
# puts item
|
77
|
-
# # => {'bbb' => 222}
|
78
|
-
#
|
79
|
-
# # Sending the item back is required, or a new one
|
80
|
-
# # in case you want to replace item to insert.
|
81
|
-
# item
|
82
|
-
# end
|
83
|
-
# data << {'bbb' => 222}
|
84
|
-
# data
|
85
|
-
# # => [{'aaa' => 111, 'bbb' => 222}]
|
86
|
-
#
|
87
|
-
# @example before_match
|
88
|
-
# keys = ['id']
|
89
|
-
# defaults = {'aaa' => 111}
|
90
|
-
# values = [
|
91
|
-
# {'id' => 1, 'ccc' => 333}
|
92
|
-
# ]
|
93
|
-
# collection = SmartCollection.new keys,
|
94
|
-
# defaults: defaults
|
95
|
-
# values: values
|
96
|
-
# collection.bind_event(:before_match) do |collection, item|
|
97
|
-
# puts collection
|
98
|
-
# # => [{'id' => 1, 'aaa' => 111, 'ccc' => 333}]
|
99
|
-
# puts item
|
100
|
-
# # => {'id' => 1, 'aaa' => 111, 'bbb' => 222}
|
101
|
-
#
|
102
|
-
# # Sending the item back is required, or a new one
|
103
|
-
# # in case you want to replace item to insert.
|
104
|
-
# item
|
105
|
-
# end
|
106
|
-
# data << {'id' => 1, 'bbb' => 222}
|
107
|
-
# data
|
108
|
-
# # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
|
109
|
-
#
|
110
|
-
# @example before_insert
|
111
|
-
# keys = ['id']
|
112
|
-
# defaults = {'aaa' => 111}
|
113
|
-
# values = [
|
114
|
-
# {'id' => 1, 'ccc' => 333}
|
115
|
-
# ]
|
116
|
-
# collection = SmartCollection.new keys,
|
117
|
-
# defaults: defaults
|
118
|
-
# values: values
|
119
|
-
# collection.bind_event(:before_insert) do |collection, item, match|
|
120
|
-
# puts collection
|
121
|
-
# # => [{'id' => 1, 'aaa' => 111, 'ccc' => 333}]
|
122
|
-
# puts item
|
123
|
-
# # => {'id' => 1, 'aaa' => 111, 'bbb' => 222}
|
124
|
-
# puts match
|
125
|
-
# # => {'id' => 1, 'aaa' => 111, 'ccc' => 333}
|
126
|
-
#
|
127
|
-
# # Sending the item back is required, or a new one
|
128
|
-
# # in case you want to replace item to insert.
|
129
|
-
# item
|
130
|
-
# end
|
131
|
-
# data << {'id' => 1, 'bbb' => 222}
|
132
|
-
# data
|
133
|
-
# # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
|
134
|
-
#
|
135
|
-
# @example after_insert
|
136
|
-
# keys = ['id']
|
137
|
-
# defaults = {'aaa' => 111}
|
138
|
-
# values = [
|
139
|
-
# {'id' => 1, 'ccc' => 333}
|
140
|
-
# ]
|
141
|
-
# collection = SmartCollection.new keys,
|
142
|
-
# defaults: defaults
|
143
|
-
# values: values
|
144
|
-
# collection.bind_event(:after_insert) do |collection, item, match|
|
145
|
-
# puts collection
|
146
|
-
# # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
|
147
|
-
# puts item
|
148
|
-
# # => {'id' => 1, 'aaa' => 111, 'bbb' => 222}
|
149
|
-
# puts match
|
150
|
-
# # => {'id' => 1, 'aaa' => 111, 'ccc' => 333}
|
151
|
-
# # No need to send item back since it is already inserted
|
152
|
-
# end
|
153
|
-
# data << {'id' => 1, 'bbb' => 222}
|
154
|
-
# data
|
155
|
-
# # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
|
156
|
-
#
|
157
|
-
# @note Some events will expect a return value to replace item on insertion:
|
158
|
-
# * `before_match`
|
159
|
-
# * `before_defaults`
|
160
|
-
# * `before_insert`
|
161
|
-
def bind_event key, &block
|
162
|
-
unless EVENTS.include? key
|
163
|
-
raise ArgumentError.new("Unknown event '#{key}'")
|
164
|
-
end
|
165
|
-
(events[key] ||= []) << block
|
166
|
-
end
|
167
|
-
|
168
|
-
# Call an event
|
169
|
-
# @private
|
170
|
-
#
|
171
|
-
# @param [Symbol] key Event name.
|
172
|
-
# @param default Detault return value when event's return nil.
|
173
|
-
# @param args event arguments.
|
174
|
-
def call_event key, default = nil, *args
|
175
|
-
return default if events[key].nil?
|
176
|
-
result = nil
|
177
|
-
events[key].each{|event| result = event.call self, *args}
|
178
|
-
result.nil? ? default : result
|
179
|
-
end
|
180
|
-
|
181
|
-
# Check whenever two items keys match.
|
182
|
-
#
|
183
|
-
# @param [Hash] item_a Item to match.
|
184
|
-
# @param [Hash] item_b Item to match.
|
185
|
-
#
|
186
|
-
# @return [Boolean]
|
187
|
-
def match_keys? item_a, item_b
|
188
|
-
return false if key_fields.nil? || key_fields.count < 1
|
189
|
-
return true if item_a.nil? && item_b.nil?
|
190
|
-
return false if item_a.nil? || item_b.nil?
|
191
|
-
key_fields.each do |key|
|
192
|
-
return false if item_a[key] != item_b[key]
|
193
|
-
end
|
194
|
-
true
|
195
|
-
end
|
196
|
-
|
197
|
-
# Apply default values into item.
|
198
|
-
# @private
|
199
|
-
#
|
200
|
-
# @param [Hash] item Item to apply defaults.
|
201
|
-
#
|
202
|
-
# @return [Hash] Item
|
203
|
-
def apply_defaults item
|
204
|
-
defaults.each do |key, value|
|
205
|
-
next unless item[key].nil?
|
206
|
-
item[key] = value.respond_to?(:call) ? value.call(item) : value
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Find an item by matching filter keys
|
211
|
-
#
|
212
|
-
# @param [Hash] filter
|
213
|
-
#
|
214
|
-
# @return [Hash,nil] First existing item match or nil when no match.
|
215
|
-
#
|
216
|
-
# @note _Warning:_ It uses table scan to filter and will be slow.
|
217
|
-
def find_match filter
|
218
|
-
self.find do |item|
|
219
|
-
match_keys? item, filter
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Add/remplace an item avoiding duplicates
|
224
|
-
def << item
|
225
|
-
item = call_event :before_defaults, item, item
|
226
|
-
apply_defaults item
|
227
|
-
item = call_event :before_match, item, item
|
228
|
-
match = find_match item
|
229
|
-
item = call_event :before_insert, item, item, match
|
230
|
-
delete match unless match.nil?
|
231
|
-
result = super(item)
|
232
|
-
call_event :after_insert, result, item, match
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|