ae_easy-core 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|