esse 0.0.3 → 0.1.2

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/exec/esse +3 -1
  3. data/lib/esse/backend/index/aliases.rb +8 -4
  4. data/lib/esse/backend/index/close.rb +6 -5
  5. data/lib/esse/backend/index/create.rb +20 -9
  6. data/lib/esse/backend/index/delete.rb +15 -14
  7. data/lib/esse/backend/index/documents.rb +2 -2
  8. data/lib/esse/backend/index/existance.rb +2 -3
  9. data/lib/esse/backend/index/open.rb +6 -5
  10. data/lib/esse/backend/index/refresh.rb +43 -0
  11. data/lib/esse/backend/index/reset.rb +33 -0
  12. data/lib/esse/backend/index/update.rb +37 -15
  13. data/lib/esse/backend/index.rb +18 -4
  14. data/lib/esse/backend/index_type/documents.rb +53 -42
  15. data/lib/esse/backend/index_type.rb +7 -2
  16. data/lib/esse/cli/event_listener.rb +87 -0
  17. data/lib/esse/cli/generate.rb +9 -4
  18. data/lib/esse/cli/index/base_operation.rb +76 -0
  19. data/lib/esse/cli/index/close.rb +26 -0
  20. data/lib/esse/cli/index/create.rb +26 -0
  21. data/lib/esse/cli/index/delete.rb +26 -0
  22. data/lib/esse/cli/index/import.rb +26 -0
  23. data/lib/esse/cli/index/open.rb +26 -0
  24. data/lib/esse/cli/index/reset.rb +26 -0
  25. data/lib/esse/cli/index/update_aliases.rb +32 -0
  26. data/lib/esse/cli/index/update_mapping.rb +33 -0
  27. data/lib/esse/cli/index/update_settings.rb +26 -0
  28. data/lib/esse/cli/index.rb +78 -2
  29. data/lib/esse/cli/templates/config.rb.erb +20 -0
  30. data/lib/esse/cli/templates/index.rb.erb +76 -11
  31. data/lib/esse/cli/templates/type_collection.rb.erb +41 -0
  32. data/lib/esse/cli/templates/{mappings.json → type_mappings.json} +0 -0
  33. data/lib/esse/cli/templates/type_serializer.rb.erb +23 -0
  34. data/lib/esse/cli.rb +75 -3
  35. data/lib/esse/cluster.rb +22 -6
  36. data/lib/esse/config.rb +39 -5
  37. data/lib/esse/core.rb +18 -36
  38. data/lib/esse/errors.rb +47 -0
  39. data/lib/esse/events/bus.rb +103 -0
  40. data/lib/esse/events/event.rb +64 -0
  41. data/lib/esse/events/publisher.rb +119 -0
  42. data/lib/esse/events.rb +49 -0
  43. data/lib/esse/index/backend.rb +2 -1
  44. data/lib/esse/index/base.rb +4 -6
  45. data/lib/esse/index/mappings.rb +2 -3
  46. data/lib/esse/index/settings.rb +7 -8
  47. data/lib/esse/index.rb +2 -1
  48. data/lib/esse/index_mapping.rb +2 -2
  49. data/lib/esse/index_setting.rb +8 -4
  50. data/lib/esse/index_type/actions.rb +2 -1
  51. data/lib/esse/index_type/backend.rb +2 -1
  52. data/lib/esse/index_type/mappings.rb +2 -2
  53. data/lib/esse/index_type.rb +6 -1
  54. data/lib/esse/logging.rb +19 -0
  55. data/lib/esse/object_document_mapper.rb +96 -0
  56. data/lib/esse/primitives/hash_utils.rb +40 -0
  57. data/lib/esse/primitives/hstring.rb +4 -3
  58. data/lib/esse/primitives/output.rb +64 -0
  59. data/lib/esse/primitives.rb +1 -0
  60. data/lib/esse/template_loader.rb +1 -1
  61. data/lib/esse/version.rb +1 -1
  62. data/lib/esse.rb +12 -3
  63. metadata +124 -21
  64. data/.gitignore +0 -12
  65. data/.rubocop.yml +0 -128
  66. data/CHANGELOG.md +0 -0
  67. data/Gemfile +0 -7
  68. data/Gemfile.lock +0 -60
  69. data/LICENSE.txt +0 -21
  70. data/README.md +0 -52
  71. data/Rakefile +0 -4
  72. data/bin/console +0 -22
  73. data/bin/setup +0 -8
  74. data/esse.gemspec +0 -39
  75. data/lib/esse/cli/templates/serializer.rb.erb +0 -14
  76. data/lib/esse/index_type/serializer.rb +0 -87
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_operation'
4
+
5
+ module Esse
6
+ module CLI
7
+ class Index::UpdateSettings < Index::BaseOperation
8
+ def run
9
+ validate_options!
10
+ indices.each do |index|
11
+ index.elasticsearch.update_settings!(**options)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def options
18
+ @options.slice(*@options.keys - CLI_IGNORE_OPTS)
19
+ end
20
+
21
+ def validate_options!
22
+ validate_indices_option!
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,12 +2,88 @@
2
2
 
3
3
  require 'thor'
4
4
  require_relative 'base'
5
+
5
6
  module Esse
6
7
  module CLI
7
8
  class Index < Base
8
- desc 'create *INDEX_CLASSES', 'Creates a new index'
9
+ desc 'reset *INDEX_CLASSES', 'Performs zero-downtime index resetting.'
10
+ long_desc <<-DESC
11
+ This task is used to rebuild index with zero-downtime. The task will:
12
+ * Creates a new index using the suffix defined on index class or from CLI.
13
+ * Import documents using the Index collection.
14
+ * Update alias to point to the new index.
15
+ * Delete the old index.
16
+ DESC
17
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
18
+ def reset(*index_classes)
19
+ require_relative 'index/reset'
20
+ Reset.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
21
+ end
22
+
23
+ desc 'create *INDEX_CLASSES', 'Creates indices for the given classes'
24
+ long_desc <<-DESC
25
+ Creates index and applies mapping and settings for the given classes.
26
+
27
+ Indices are created with the following naming convention:
28
+ <cluster.index_prefix>_<index_class.index_name>_<index_class.index_version>.
29
+ DESC
30
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
31
+ option :alias, type: :boolean, default: false, aliases: '-a', desc: 'Update alias after create index'
9
32
  def create(*index_classes)
10
- # Add action here
33
+ require_relative 'index/create'
34
+ Create.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
35
+ end
36
+
37
+ desc 'delete *INDEX_CLASSES', 'Deletes indices for the given classes'
38
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
39
+ def delete(*index_classes)
40
+ require_relative 'index/delete'
41
+ Delete.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
42
+ end
43
+
44
+ desc 'update_aliases *INDEX_CLASS', 'Replaces all existing aliases by the given suffix'
45
+ option :suffix, type: :string, aliases: '-s', desc: 'Suffix to append to index name'
46
+ def update_aliases(*index_classes)
47
+ require_relative 'index/update_aliases'
48
+ UpdateAliases.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
49
+ end
50
+
51
+ desc 'update_settings *INDEX_CLASS', 'Closes the index for read/write operations, updates the index settings, and open it again'
52
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
53
+ option :type, type: :string, default: nil, aliases: '-t', desc: 'Document Type to update mapping for'
54
+ def update_settings(*index_classes)
55
+ require_relative 'index/update_settings'
56
+ UpdateSettings.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
57
+ end
58
+
59
+ desc 'update_mapping *INDEX_CLASS', 'Create or update a mapping'
60
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
61
+ option :type, type: :string, default: nil, aliases: '-t', desc: 'Document Type to update mapping for'
62
+ def update_mapping(*index_classes)
63
+ require_relative 'index/update_mapping'
64
+ UpdateMapping.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
65
+ end
66
+
67
+ desc 'close *INDEX_CLASS', 'Close an index (keep the data on disk, but deny operations with the index).'
68
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
69
+ def close(*index_classes)
70
+ require_relative 'index/close'
71
+ Close.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
72
+ end
73
+
74
+ desc 'open *INDEX_CLASS', 'Open a previously closed index.'
75
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
76
+ def open(*index_classes)
77
+ require_relative 'index/open'
78
+ Open.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
79
+ end
80
+
81
+ desc 'import *INDEX_CLASSES', 'Import documents from the given classes'
82
+ option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
83
+ option :context, type: :hash, default: {}, required: true, desc: 'List of options to pass to the index class'
84
+ def import(*index_classes)
85
+ require_relative 'index/import'
86
+ Import.new(indices: index_classes, **HashUtils.deep_transform_keys(options.to_h, &:to_sym)).run
11
87
  end
12
88
  end
13
89
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Esse.configure do |config|
4
+ config.indices_directory = File.expand_path('../../app/indices', __FILE__)
5
+ config.cluster(:default) do |cluster|
6
+ # Default index prefix
7
+ cluster.index_prefix = "esse"
8
+ # you also can add environment to the index prefix if available:
9
+ # cluster.index_prefix = "esse_#{ENV['RACK_ENV']}"
10
+ #
11
+ # Initialize the ElastiSearch client to be used by the default cluster
12
+ cluster.client = Elasticsearch::Client.new
13
+
14
+ # Global index settings
15
+ # cluster.index_settings = {
16
+ # number_of_shards: 5,
17
+ # number_of_replicas: 0,
18
+ # }
19
+ end
20
+ end
@@ -1,11 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ <%- @types.each do |type| -%>
4
+ require_relative '<%= @index_name.demodulize.underscore.to_s %>/collections/<%= type.underscore %>_collection'
5
+ <%- end -%>
6
+ <%- @types.each do |type| -%>
7
+ require_relative '<%= @index_name.demodulize.underscore.to_s %>/serializers/<%= type.underscore %>_serializer'
8
+ <%- end -%>
9
+
3
10
  class <%= @index_name %> < <%= @base_class %>
4
11
  # plugin :active_record
5
12
  # plugin :sequel
6
- <%- @types.each do |type| -%>
7
13
 
8
- define_type :<%= type %> do
14
+ <%- if @types.empty? -%>
15
+ # Collection
16
+ # ==========
17
+ #
18
+ # Collection is the source of data for the index. We hightly recommend using a
19
+ # another class that implements the Enumerable interface to better split the
20
+ # responsabilities and easy to test.
21
+ #
22
+ # collection Collections::DocumentCollection
23
+ #
24
+ # but you can also use block definition style:
25
+ # collection do |_context, &block|
26
+ # block.call [{ title: 'foo' }, { title: 'bar' }], context
27
+ # end
28
+ #
29
+ # Serializer block or class yielder should be called with an array of objects.
30
+ # Each these objects should be serialized using the serializer in described in the next section.
31
+ # The number of objects will be indexed to elasticsearch using the bulk api. So adjust the
32
+ # number of objects to be indexed accordingly.
33
+ #
34
+ # Here is a good place to eager loading data from database or any other repository.
35
+ # The bellow example is a rails like application that could preload using activerecord
36
+ #
37
+ # collection do |**context, &block|
38
+ # query = <%= @index_name.camelize %>.all
39
+ # query = query.where(**context[:conditions]) if context[:conditions]
40
+ # query = query.includes(:user) if context[:include_user]
41
+ # query.find_in_batches(batch_size: 5000) do |batch|
42
+ # block.call batch, context
43
+ # end
44
+ # end
45
+
46
+
47
+ # Serializer
48
+ # ==========
49
+ #
50
+ # Serializer is the class responsible for serializing indexing documents.
51
+ # Each object yielded by the collection will be serialized on this step.
52
+ # We recommend using a another class to handle the serialization. The only requirement is
53
+ # that the class implements the `#to_h` method.
54
+ # The serializer class may also be initialized with context if collection block is called with that extra parameter.
55
+ # Ability to call serializer with context is useful for preloading data from database or any other repository.
56
+ #
57
+ # serializer Serializers::DocumentSerializer
58
+ #
59
+ # You can also serialize the collection entry using a block:
60
+ #
61
+ # serializer do |model, context|
62
+ # hash = {
63
+ # name: <%= @index_name.underscore %>.name,
64
+ # }
65
+ # # Context is just an example here. But it's useful for eager loading data.
66
+ # # I'll think a better example when implement this idea.
67
+ # hash[:some_attribute] = <%= @index_name.underscore %>.some_attribute if context[:include_some_attribute]
68
+ # hash
69
+ # end
70
+ <%- end -%>
71
+ <%- @types.each do |type| -%>
72
+ define_type :<%= type.underscore %> do
9
73
  # Collection
10
74
  # ==========
11
75
  #
@@ -14,19 +78,19 @@ class <%= @index_name %> < <%= @base_class %>
14
78
  # Useful for eager loading data from database or any other repository. Below is an example of a rails like
15
79
  # application could load using activerecord.
16
80
  #
17
- # collection do |conditions|
18
- # context = {}
19
- # <%= type.camelize %>.where(conditions).find_in_batches(batch_size: 5000) do |batch|
20
- # yield batch, context, ...
81
+ # collection do |context, &block|
82
+ # <%= type.camelize %>.where(context[:conditions]).find_in_batches(batch_size: 5000) do |batch|
83
+ # block.call batch, context
21
84
  # end
22
85
  # end
86
+ collection Collections::<%= type.camelize %>Collection
23
87
  #
24
88
  #
25
89
  # Serializer
26
90
  # ==========
27
91
  #
28
- # The serializer can be any class that respond with the `as_json` class method.
29
- # And the result of its as_json is a Hash.
92
+ # The serializer can be any class that respond with the `to_h` class method.
93
+ # And the result of its to_h is a Hash.
30
94
  #
31
95
  # Here is an example of a simple serializer:
32
96
  # app/serializers/<%= type %>_serializer.rb
@@ -35,17 +99,17 @@ class <%= @index_name %> < <%= @base_class %>
35
99
  # @<%= type %> = <%= type %>
36
100
  # end
37
101
  #
38
- # def as_json
102
+ # def to_h
39
103
  # { '_id' => @<%= type %>.id, 'name' => @<%= type %>.name }
40
104
  # end
41
105
  # end
42
106
  #
43
107
  # And here you specify your serializer classe.
44
- # serializer <%= @index_name %>::Serializers::<%= type.camelize %>Serializer
108
+ # serializer Serializers::<%= type.camelize %>Serializer
45
109
  #
46
110
  # You can also serialize the collection entry using a block:
47
111
  #
48
- # serializer(<%= type %>, context = {}) do
112
+ # serializer do |model, **context|
49
113
  # hash = {
50
114
  # name: <%= type %>.name,
51
115
  # }
@@ -54,6 +118,7 @@ class <%= @index_name %> < <%= @base_class %>
54
118
  # hash[:some_attribute] = <%= type %>.some_attribute if context[:include_some_attribute]
55
119
  # hash
56
120
  # end
121
+ serializer Serializers::<%= type.camelize %>Serializer
57
122
  end
58
123
  <%- end -%>
59
124
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @index_name %> < <%= @base_class %>
4
+ module Collections
5
+ class <%= @type.camelize %>Collection
6
+ include Enumerable
7
+
8
+ # @param params [Hash] List of parameters
9
+ def initialize(**params)
10
+ @params = params
11
+ end
12
+
13
+ # Find all <%= @type %> in batches
14
+ #
15
+ # @yield [Array<<%= @type.camelize %>>]
16
+ # @see <%= @index_name %>::<%= @type.camelize %>#collection
17
+ def each
18
+ offset = 0
19
+ while (rows = find_all(offset))
20
+ break if rows.none?
21
+
22
+ # You may preload associations before serialize them
23
+ # associations = preload_associations!(rows)
24
+ # yield(row, associations)
25
+ offset += 1
26
+
27
+ yield(rows, **params)
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ attr_reader :params
34
+
35
+ # @param offset [Number] Offset to start from
36
+ def find_all(offset)
37
+ # @TODO load data from persistent store
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @index_name %> < <%= @base_class %>
4
+ module Serializers
5
+ class <%= @type.camelize %>Serializer
6
+ def initialize(<%= @type %>, **params)
7
+ @entity = <%= @type %>
8
+ @params = params
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ id: entity.id, # This field is required
14
+ name: entity.name,
15
+ }
16
+ end
17
+
18
+ protected
19
+
20
+ attr_reader :entity, :params
21
+ end
22
+ end
23
+ end
data/lib/esse/cli.rb CHANGED
@@ -2,16 +2,48 @@
2
2
 
3
3
  require 'thor'
4
4
 
5
+ require_relative 'primitives/output'
5
6
  require_relative 'cli/index'
6
7
  require_relative 'cli/generate'
8
+ require_relative 'cli/event_listener'
7
9
 
8
10
  module Esse
9
11
  module CLI
10
- def self.start(*args)
11
- Root.start(*args)
12
+ class << self
13
+ def start(*args)
14
+ Root.start(*args)
15
+ end
16
+
17
+ def with_friendly_errors
18
+ yield
19
+ rescue CLI::Error => e
20
+ Output.print_error(e)
21
+ exit(1)
22
+ end
12
23
  end
13
24
 
14
25
  class Root < Thor
26
+ include Thor::Actions
27
+
28
+ class_option :require, type: :string, aliases: '-r', required: false,
29
+ default: nil, desc: 'Require config file where the application is defined'
30
+ class_option :silent, type: :boolean, aliases: '-s', required: false, default: false, desc: 'Silent mode'
31
+
32
+ def initialize(*)
33
+ super
34
+
35
+ load_app_config(options[:require])
36
+ setup_listeners if !options[:silent] && Esse.config.cli_event_listeners?
37
+ end
38
+
39
+ def self.source_root
40
+ File.expand_path('../cli', __FILE__)
41
+ end
42
+
43
+ def self.exit_on_failure?
44
+ true
45
+ end
46
+
15
47
  map %w[--version -v] => :version
16
48
 
17
49
  desc 'index SUBCOMMAND ...ARGS', 'Manage indices'
@@ -22,7 +54,47 @@ module Esse
22
54
 
23
55
  desc '--version, -v', 'Show package version'
24
56
  def version
25
- puts format('Esse version: %<version>s', version: Esse::VERSION)
57
+ Output.print_message('Esse version: %<version>s', version: Esse::VERSION)
58
+ end
59
+
60
+ desc 'install', 'Generate boilerplate configuration files'
61
+ option :path, type: :string, aliases: '-p', required: false, default: './'
62
+ def install
63
+ path = Pathname.new(File.expand_path(options[:path], Dir.pwd))
64
+ path = path.dirname unless path.directory?
65
+ @app_dir = path.basename
66
+ template(
67
+ 'templates/config.rb.erb',
68
+ path.join('config/esse.rb'),
69
+ )
70
+ end
71
+
72
+ private
73
+
74
+ def setup_listeners
75
+ Esse::Events.__bus__.events.keys.grep(/^elasticsearch/).each do |event_name|
76
+ Esse::Events.subscribe(event_name) do |event|
77
+ EventListener[event_name]&.call(event)
78
+ end
79
+ end
80
+ end
81
+
82
+ def load_app_config(path)
83
+ if path.nil?
84
+ CLI_CONFIG_PATHS.each do |config_path|
85
+ next unless File.exist?(config_path)
86
+ path = config_path
87
+ break
88
+ end
89
+ end
90
+ return unless path
91
+
92
+ begin
93
+ Output.print_message('Loading configuration file: %<path>s', path: path)
94
+ load path
95
+ rescue LoadError => e
96
+ raise InvalidOption, e.message
97
+ end
26
98
  end
27
99
  end
28
100
  end
data/lib/esse/cluster.rb CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  module Esse
4
4
  class Cluster
5
- ATTRIBUTES = %i[index_prefix index_settings client].freeze
5
+ ATTRIBUTES = %i[index_prefix index_settings client wait_for_status].freeze
6
+ WAIT_FOR_STATUSES = %w[green yellow red].freeze
6
7
 
7
8
  # The index prefix. For example an index named UsersIndex.
8
9
  # With `index_prefix = 'app1'`. Final index/alias is: 'app1_users'
@@ -11,6 +12,15 @@ module Esse
11
12
  # This settings will be passed through all indices during the mapping
12
13
  attr_accessor :index_settings
13
14
 
15
+ # if this option set, actions such as creating or deleting index,
16
+ # importing data will wait for the status specified. Extremely useful
17
+ # for tests under heavy indices manipulations.
18
+ # Value can be set to `red`, `yellow` or `green`.
19
+ #
20
+ # Example:
21
+ # wait_for_status: green
22
+ attr_accessor :wait_for_status
23
+
14
24
  attr_reader :id
15
25
 
16
26
  def initialize(id:, **options)
@@ -35,14 +45,14 @@ module Esse
35
45
  end
36
46
 
37
47
  # Define the elasticsearch client connectio
38
- # @param client [Elasticsearch::Client, Hash] an instance of elasticsearch/api client or an hash
48
+ # @param es_client [Elasticsearch::Client, Hash] an instance of elasticsearch/api client or an hash
39
49
  # with the settings that will be used to initialize Elasticsearch::Client
40
- def client=(val)
41
- @client = if val.is_a?(Hash)
42
- settings = val.each_with_object({}) { |(k,v), r| r[k.to_sym] = v }
50
+ def client=(es_client)
51
+ @client = if es_client.is_a?(Hash)
52
+ settings = es_client.each_with_object({}) { |(k, v), r| r[k.to_sym] = v }
43
53
  Elasticsearch::Client.new(settings)
44
54
  else
45
- val
55
+ es_client
46
56
  end
47
57
  end
48
58
 
@@ -54,5 +64,11 @@ module Esse
54
64
  attrs << format('client=%p', @client)
55
65
  format('#<Esse::Cluster %<attrs>s>', attrs: attrs.join(' '))
56
66
  end
67
+
68
+ def wait_for_status!(status: wait_for_status)
69
+ return unless WAIT_FOR_STATUSES.include?(status.to_s)
70
+
71
+ client.cluster.health(wait_for_status: status.to_s)
72
+ end
57
73
  end
58
74
  end
data/lib/esse/config.rb CHANGED
@@ -3,6 +3,31 @@
3
3
  require 'pathname'
4
4
 
5
5
  module Esse
6
+ # Block configurations
7
+ # Esse.configure do |conf|
8
+ # conf.indices_directory = 'app/indices/directory'
9
+ # conf.cluster(:v1) do |cluster|
10
+ # cluster.index_prefix = 'backend'
11
+ # cluster.client = Elasticsearch::Client.new
12
+ # cluster.index_settings = {
13
+ # number_of_shards: 2,
14
+ # number_of_replicas: 0
15
+ # }
16
+ # end
17
+ # end
18
+ #
19
+ # Inline configurations
20
+ # Esse.config.indices_directory = 'app/indices/directory'
21
+ # Esse.config.cluster(:v1).client = Elasticsearch::Client.new
22
+ class << self
23
+ def config
24
+ @config ||= Config.new
25
+ yield(@config) if block_given?
26
+ @config
27
+ end
28
+ alias_method :configure, :config
29
+ end
30
+
6
31
  # Provides all configurations
7
32
  #
8
33
  # Example
@@ -19,14 +44,14 @@ module Esse
19
44
  def initialize
20
45
  self.indices_directory = 'app/indices'
21
46
  @clusters = {}
22
- clusters(DEFAULT_CLUSTER_ID) # initialize the :default client
47
+ cluster(DEFAULT_CLUSTER_ID) # initialize the :default client
23
48
  end
24
49
 
25
50
  def cluster_ids
26
51
  @clusters.keys
27
52
  end
28
53
 
29
- def clusters(key = DEFAULT_CLUSTER_ID, **options)
54
+ def cluster(key = DEFAULT_CLUSTER_ID, **options)
30
55
  return unless key
31
56
 
32
57
  id = key.to_sym
@@ -35,6 +60,7 @@ module Esse
35
60
  yield c if block_given?
36
61
  end
37
62
  end
63
+ alias_method :clusters, :cluster
38
64
 
39
65
  def indices_directory=(value)
40
66
  @indices_directory = value.is_a?(Pathname) ? value : Pathname.new(value)
@@ -45,14 +71,22 @@ module Esse
45
71
  when Hash
46
72
  assign(arg)
47
73
  when File, Pathname
48
- # @TODO Load JSON or YAML
74
+ assign(YAML.load_file(arg))
49
75
  when String
50
- # @TODO Load JSON or YAML if File.exist?(arg)
76
+ return load(Pathname.new(arg)) if File.exist?(arg)
77
+
78
+ assign(YAML.safe_load(arg))
51
79
  else
52
80
  raise ArgumentError, printf('could not load configuration using: %p', val)
53
81
  end
54
82
  end
55
83
 
84
+ # :nodoc:
85
+ # This is only used by rspec to disable the CLI print out.
86
+ def cli_event_listeners?
87
+ true
88
+ end
89
+
56
90
  private
57
91
 
58
92
  def assign(hash)
@@ -64,7 +98,7 @@ module Esse
64
98
  end
65
99
  if (connections = hash['clusters'] || hash[:clusters]).is_a?(Hash)
66
100
  connections.each do |key, value|
67
- clusters(key).assign(value) if value.is_a?(Hash)
101
+ cluster(key).assign(value) if value.is_a?(Hash)
68
102
  end
69
103
  end
70
104
  true
data/lib/esse/core.rb CHANGED
@@ -1,35 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  require 'multi_json'
4
5
  require 'elasticsearch'
5
6
 
6
7
  module Esse
8
+ require_relative 'config'
9
+ require_relative 'cluster'
10
+ require_relative 'primitives'
11
+ require_relative 'index_type'
12
+ require_relative 'index_setting'
13
+ require_relative 'index_mapping'
14
+ require_relative 'template_loader'
15
+ require_relative 'backend/index'
16
+ require_relative 'backend/index_type'
17
+ require_relative 'version'
18
+ require_relative 'logging'
19
+ require_relative 'events'
20
+ include Logging
21
+
7
22
  @single_threaded = false
8
23
  # Mutex used to protect mutable data structures
9
24
  @data_mutex = Mutex.new
10
25
 
11
- # Block configurations
12
- # Esse.config do |conf|
13
- # conf.indices_directory = 'app/indices/directory'
14
- # conf.clusters(:v1) do |cluster|
15
- # cluster.index_prefix = 'backend'
16
- # cluster.client = Elasticsearch::Client.new
17
- # cluster.index_settings = {
18
- # number_of_shards: 2,
19
- # number_of_replicas: 0
20
- # }
21
- # end
22
- # end
23
- #
24
- # Inline configurations
25
- # Esse.config.indices_directory = 'app/indices/directory'
26
- # Esse.config.clusters(:v1).client = Elasticsearch::Client.new
27
- def self.config
28
- @config ||= Config.new
29
- yield(@config) if block_given?
30
- @config
31
- end
32
-
33
26
  # Unless in single threaded mode, protects access to any mutable
34
27
  # global data structure in Esse.
35
28
  # Uses a non-reentrant mutex, so calling code should be careful.
@@ -48,9 +41,9 @@ module Esse
48
41
 
49
42
  # Simple helper used to fetch Hash value using Symbol and String keys.
50
43
  #
51
- # @param [Hash] the JSON document
52
- # @option [Array] :delete Removes the hash key and return its value
53
- # @option [Array] :keep Fetch the hash key and return its value
44
+ # @param hash [Hash] the JSON document
45
+ # @param delete [Array] Removes the hash key and return its value
46
+ # @param keep [Array] Fetch the hash key and return its value
54
47
  # @return [Array([Integer, String, nil], Hash)] return the key value and the modified hash
55
48
  def self.doc_id!(hash, delete: %w[_id], keep: %w[id])
56
49
  return unless hash.is_a?(Hash)
@@ -75,15 +68,4 @@ module Esse
75
68
  end
76
69
  [id, modified]
77
70
  end
78
-
79
- require_relative 'config'
80
- require_relative 'cluster'
81
- require_relative 'primitives'
82
- require_relative 'index_type'
83
- require_relative 'index_setting'
84
- require_relative 'index_mapping'
85
- require_relative 'template_loader'
86
- require_relative 'backend/index'
87
- require_relative 'backend/index_type'
88
- require_relative 'version'
89
71
  end