elastics-client 1.0.4

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 (47) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +28 -0
  3. data/VERSION +1 -0
  4. data/elastics-client.gemspec +33 -0
  5. data/lib/elastics.rb +108 -0
  6. data/lib/elastics/api_stubs.rb +1268 -0
  7. data/lib/elastics/api_templates/cluster_api.yml +94 -0
  8. data/lib/elastics/api_templates/core_api.yml +202 -0
  9. data/lib/elastics/api_templates/indices_api.yml +304 -0
  10. data/lib/elastics/class_proxy/base.rb +29 -0
  11. data/lib/elastics/class_proxy/templates.rb +97 -0
  12. data/lib/elastics/class_proxy/templates/doc.rb +91 -0
  13. data/lib/elastics/class_proxy/templates/search.rb +72 -0
  14. data/lib/elastics/configuration.rb +25 -0
  15. data/lib/elastics/deprecation.rb +153 -0
  16. data/lib/elastics/errors.rb +43 -0
  17. data/lib/elastics/http_clients/base.rb +15 -0
  18. data/lib/elastics/http_clients/loader.rb +51 -0
  19. data/lib/elastics/http_clients/patron.rb +29 -0
  20. data/lib/elastics/http_clients/rest_client.rb +36 -0
  21. data/lib/elastics/logger.rb +37 -0
  22. data/lib/elastics/prog_bar.rb +39 -0
  23. data/lib/elastics/rails.rb +1 -0
  24. data/lib/elastics/result.rb +24 -0
  25. data/lib/elastics/result/bulk.rb +20 -0
  26. data/lib/elastics/result/document.rb +67 -0
  27. data/lib/elastics/result/multi_get.rb +24 -0
  28. data/lib/elastics/result/search.rb +28 -0
  29. data/lib/elastics/struct/array.rb +25 -0
  30. data/lib/elastics/struct/hash.rb +105 -0
  31. data/lib/elastics/struct/paginable.rb +58 -0
  32. data/lib/elastics/struct/prunable.rb +60 -0
  33. data/lib/elastics/struct/symbolize.rb +27 -0
  34. data/lib/elastics/tasks.rb +62 -0
  35. data/lib/elastics/template.rb +124 -0
  36. data/lib/elastics/template/common.rb +42 -0
  37. data/lib/elastics/template/logger.rb +66 -0
  38. data/lib/elastics/template/partial.rb +28 -0
  39. data/lib/elastics/template/search.rb +30 -0
  40. data/lib/elastics/template/slim_search.rb +13 -0
  41. data/lib/elastics/template/tags.rb +56 -0
  42. data/lib/elastics/templates.rb +20 -0
  43. data/lib/elastics/utility_methods.rb +131 -0
  44. data/lib/elastics/utils.rb +103 -0
  45. data/lib/elastics/variables.rb +62 -0
  46. data/lib/tasks.rake +28 -0
  47. metadata +148 -0
@@ -0,0 +1,66 @@
1
+ module Elastics
2
+ class Template
3
+ module Logger
4
+
5
+ def caller_line
6
+ caller.find{|l| l !~ /(#{LIB_PATHS.join('|')})/}
7
+ end
8
+
9
+ private
10
+
11
+ def template_name
12
+ @host_elastics && @name && "#{@host_elastics.context}.#@name" || 'template'
13
+ end
14
+
15
+ def log_render(int, path, encoded_data, result)
16
+ logger = Conf.logger
17
+ return unless logger.is_a?(Elastics::Logger)
18
+ logger.info Dye.dye("Rendered #{template_name} from: #{caller_line}", :blue, :bold)
19
+ return unless logger.level == ::Logger::DEBUG
20
+
21
+ h = {}
22
+ if logger.debug_variables
23
+ h[:variables] = int[:vars] if int
24
+ end
25
+ if logger.debug_request
26
+ h[:request] = {}
27
+ h[:request][:method] = method
28
+ h[:request][:path] = path
29
+ h[:request][:data] = begin
30
+ MultiJson.decode(encoded_data) unless encoded_data.nil?
31
+ rescue MultiJson::DecodeError
32
+ encoded_data
33
+ end
34
+ h[:request].delete(:data) if h[:request][:data].nil?
35
+ end
36
+ if logger.debug_result
37
+ h[:result] = result if result
38
+ end
39
+ logger.debug logger.curl_format ? curl_format(h[:request]) : yaml_format(h)
40
+ end
41
+
42
+ def curl_format(h)
43
+ pretty = h[:path] =~ /\?/ ? '&pretty=1' : '?pretty=1'
44
+ curl = %(curl -X#{method} "#{Conf.base_uri}#{h[:path]}#{pretty}")
45
+ if h[:data]
46
+ data = h[:data].is_a?(String) ? h[:data] : MultiJson.encode(h[:data], :pretty => true)
47
+ curl << %( -d '\n#{data}\n')
48
+ end
49
+ curl
50
+ end
51
+
52
+ def yaml_format(hash)
53
+ hash.to_yaml.split("\n").map do |l|
54
+ case l
55
+ when /^---$/
56
+ when /^( |-)/
57
+ Dye.dye(l, :blue)
58
+ when /^:(variables|request|result)/
59
+ Dye.dye(l, :magenta, :bold) + (Dye.color ? Dye.sgr(:blue) : '')
60
+ end
61
+ end.compact.join("\n") + "\n"
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ module Elastics
2
+ class Template
3
+ class Partial
4
+
5
+ include Common
6
+
7
+ def initialize(data)
8
+ @data = data
9
+ tags = Tags.new
10
+ stringified = tags.stringify(data)
11
+ @partials, @tags = tags.partial_and_tag_names
12
+ @tags_variables = tags.variables
13
+ instance_eval <<-ruby, __FILE__, __LINE__
14
+ def interpolate(vars={}, partial_assigned_vars={})
15
+ vars = Vars.new(vars, @tags_variables, partial_assigned_vars)
16
+ vars = interpolate_partials(vars)
17
+ #{stringified}
18
+ end
19
+ ruby
20
+ end
21
+
22
+ def to_source
23
+ {@name.to_s => @data}.to_yaml
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module Elastics
2
+ class Template
3
+ class Search < Template
4
+
5
+ def initialize(data, vars=nil)
6
+ super('GET', '/<<index>>/<<type>>/_search', data, vars)
7
+ end
8
+
9
+ def to_a(*vars)
10
+ a = super
11
+ 2.times{ a.delete_at 0 }
12
+ a
13
+ end
14
+
15
+ def to_msearch(*vars)
16
+ vars = Vars.new(*vars)
17
+ int = interpolate(vars, strict=true)
18
+ header = {}
19
+ header[:index] = int[:vars][:index] if int[:vars][:index]
20
+ header[:type] = int[:vars][:type] if int[:vars][:type]
21
+ [:search_type, :preferences, :routing].each do |k|
22
+ header[k] = int[:vars][k] if int[:vars][k] || int[:vars][:params] && int[:vars][:params][k]
23
+ end
24
+ data, encoded = build_data(int, vars)
25
+ "#{MultiJson.encode(header)}\n#{encoded}\n"
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Elastics
2
+ class Template
3
+ class SlimSearch < Search
4
+
5
+ # removes the fields param (no _source returned)
6
+ # the result.loaded_collection, will load the records from the db
7
+ def self.variables
8
+ super.deep_merge!(:params => {:fields => ''})
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ module Elastics
2
+ class Template
3
+ class Tags < Array
4
+
5
+ TAG_REGEXP = /<<\s*([\w\.]+)\s*(?:=([^>]*))*>>/
6
+
7
+ # tag variables are the defaults defined with the tag
8
+ # a variable could be optional, and the default could be nil
9
+ def variables
10
+ tag_variables = Vars.new
11
+ each do |t|
12
+ if t.default || t.optional
13
+ if t.name =~ /\./ # set default for nested var
14
+ tag_variables.store_nested(t.name, t.default)
15
+ else
16
+ tag_variables[t.name] = t.default
17
+ end
18
+ end
19
+ end
20
+ tag_variables
21
+ end
22
+
23
+ def stringify(structure)
24
+ structure.inspect.gsub(/(?:"#{TAG_REGEXP}"|#{TAG_REGEXP})/) do
25
+ match = $&
26
+ match =~ TAG_REGEXP
27
+ t = Tag.new($1, $2)
28
+ push t unless find{|i| i.name == t.name}
29
+ (match !~ /^"/) ? "\#{vars.get_prunable(:'#{t.name}')}" : "vars.get_prunable(:'#{t.name}')"
30
+ end
31
+ end
32
+
33
+ def partial_and_tag_names
34
+ map(&:name).partition{|n| n.to_s =~ /^_/}
35
+ end
36
+
37
+ end
38
+
39
+ class Tag
40
+
41
+ RESERVED = [:context, :path, :data, :params, :no_pruning, :raw_result, :raise]
42
+
43
+ attr_reader :optional, :name, :default
44
+
45
+ def initialize(name, default)
46
+ raise SourceError, ":#{name} is a reserved symbol and cannot be used as a tag name" \
47
+ if RESERVED.include?(name)
48
+ @name = name.to_sym
49
+ @optional = !!default
50
+ @default = YAML.load(default) unless default.nil?
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ module Elastics
2
+ module Templates
3
+
4
+ extend self
5
+ attr_accessor :contexts
6
+ @contexts = []
7
+
8
+ def self.included(context)
9
+ context.class_eval do
10
+ Elastics::Templates.contexts |= [context]
11
+ @elastics ||= ClassProxy::Base.new(context)
12
+ @elastics.extend(ClassProxy::Templates).init
13
+ def self.elastics; @elastics end
14
+ def self.template_methods; elastics.templates.keys end
15
+ eval "extend module #{context}::ElasticsTemplateMethods; self end"
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,131 @@
1
+ module Elastics
2
+ module UtilityMethods
3
+
4
+ def search(data, vars={})
5
+ Template::Search.new(data).setup(Elastics.elastics).render(vars)
6
+ end
7
+
8
+ # like Elastics.search, but it will use the Elastics::Template::SlimSearch instead
9
+ def slim_search(data, vars={})
10
+ Template::SlimSearch.new(data).setup(Elastics.elastics).render(vars)
11
+ end
12
+
13
+ %w[HEAD GET PUT POST DELETE].each do |m|
14
+ class_eval <<-ruby, __FILE__, __LINE__
15
+ def #{m}(*args)
16
+ perform '#{m}', *args
17
+ end
18
+ ruby
19
+ end
20
+
21
+ def json2yaml(json)
22
+ YAML.dump(MultiJson.decode(json))
23
+ end
24
+
25
+ def yaml2json(yaml)
26
+ MultiJson.encode(YAML.load(yaml))
27
+ end
28
+
29
+ def reload!
30
+ elastics.variables.deep_merge! Conf.variables
31
+ Templates.contexts.each {|c| c.elastics.reload!}
32
+ true
33
+ end
34
+
35
+ def doc(*args)
36
+ elastics.doc(*args)
37
+ end
38
+
39
+ def scan_search(*args, &block)
40
+ elastics.scan_search(*args, &block)
41
+ end
42
+
43
+ def scan_all(*vars, &block)
44
+ elastics.scan_search(:match_all, *vars) do |raw_result|
45
+ batch = raw_result['hits']['hits']
46
+ block.call(batch)
47
+ end
48
+ end
49
+
50
+ def dump_all(*vars, &block)
51
+ refresh_index(*vars)
52
+ scan_all({:params => {:fields => '*,_source'}}, *vars) do |batch|
53
+ batch.map!{|document| document.delete('_score'); document}
54
+ block.call(batch)
55
+ end
56
+ end
57
+
58
+ # refresh and pull the full document from the index
59
+ def dump_one(*vars)
60
+ refresh_index(*vars)
61
+ document = search_by_id({:params => {:fields => '*,_source'}}, *vars)
62
+ document.delete('_score')
63
+ document
64
+ end
65
+
66
+ # You should use Elastics.post_bulk_string if you have an already formatted bulk data-string
67
+ def post_bulk_collection(collection, options={})
68
+ raise ArgumentError, "Array expected as :collection, got #{collection.inspect}" \
69
+ unless collection.is_a?(Array)
70
+ bulk_string = ''
71
+ collection.each do |d|
72
+ bulk_string << build_bulk_string(d, options)
73
+ end
74
+ post_bulk_string(:bulk_string => bulk_string) unless bulk_string.empty?
75
+ end
76
+
77
+ def build_bulk_string(document, options={})
78
+ case document
79
+ when Hash
80
+ bulk_string_from_hash(document, options)
81
+ when Elastics::ModelIndexer, Elastics::ActiveModel
82
+ bulk_string_from_elastics(document, options)
83
+ else
84
+ raise NotImplementedError, "Unable to convert the document #{document.inspect} to a bulk string."
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def perform(*args)
91
+ Template.new(*args).setup(Elastics.elastics).render
92
+ end
93
+
94
+ def bulk_string_from_hash(document, options)
95
+ meta = Utils.slice_hash(document, '_index', '_type', '_id')
96
+ if document.has_key?('fields')
97
+ document['fields'].each do |k, v|
98
+ meta[k] = v if k[0] == '_'
99
+ end
100
+ end
101
+ source = document['_source'] unless options[:action] == 'delete'
102
+ to_bulk_string(meta, source, options)
103
+ end
104
+
105
+ def bulk_string_from_elastics(document, options)
106
+ elastics = document.elastics
107
+ return '' unless document.elastics_indexable?
108
+ meta = { '_index' => elastics.index,
109
+ '_type' => elastics.type,
110
+ '_id' => elastics.id }
111
+ meta['_parent'] = elastics.parent if elastics.parent
112
+ meta['_routing'] = elastics.routing if elastics.routing
113
+ source = document.elastics_source unless options[:action] == 'delete'
114
+ to_bulk_string(meta, source, options)
115
+ end
116
+
117
+ def to_bulk_string(meta, source, options)
118
+ action = options[:action] || 'index'
119
+ return '' if source.nil? || source.empty? &&! (action == 'delete')
120
+ meta['_index'] = LiveReindex.prefix_index(meta['_index']) if LiveReindex.should_prefix_index?
121
+ bulk_string = MultiJson.encode(action => meta) + "\n"
122
+ unless action == 'delete'
123
+ source_line = source.is_a?(String) ? source : MultiJson.encode(source)
124
+ return '' if source.nil? || source.empty?
125
+ bulk_string << source_line + "\n"
126
+ end
127
+ bulk_string
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,103 @@
1
+ module Elastics
2
+ module Utils
3
+ extend self
4
+
5
+ def parse_source(source)
6
+ return unless source
7
+ parsed = case source
8
+ when Hash then keyfy(:to_s, source)
9
+ when /^\s*\{.+\}\s*$/m then source
10
+ when String then YAML.load(source)
11
+ else raise ArgumentError, "expected a String or Hash instance, got #{source.inspect}"
12
+ end
13
+ raise ArgumentError, "the source does not decode to an Array, Hash or String, got #{parsed.inspect}" \
14
+ unless parsed.is_a?(Hash) || parsed.is_a?(Array) || parsed.is_a?(String)
15
+ parsed
16
+ end
17
+
18
+ def erb_process(source)
19
+ varname = "_elastics_#{source.hash.to_s.tr('-', '_')}"
20
+ ERB.new(File.read(source), nil, nil, varname).result
21
+ end
22
+
23
+ def group_array_by(ary)
24
+ h = {}
25
+ ary.each do |i|
26
+ k = yield i
27
+ if h.has_key?(k)
28
+ h[k] << i
29
+ else
30
+ h[k] = [i]
31
+ end
32
+ end
33
+ h
34
+ end
35
+
36
+ def delete_allcaps_keys(hash)
37
+ hash.keys.each { |k| hash.delete(k) if k =~ /^[A-Z_]+$/ }
38
+ hash
39
+ end
40
+
41
+ def keyfy(to_what, obj)
42
+ case obj
43
+ when Hash
44
+ h = {}
45
+ obj.each do |k,v|
46
+ h[k.send(to_what)] = keyfy(to_what, v)
47
+ end
48
+ h
49
+ when Array
50
+ obj.map{|i| keyfy(to_what, i)}
51
+ else
52
+ obj
53
+ end
54
+ end
55
+
56
+ def slice_hash(hash, *keys)
57
+ h = {}
58
+ keys.each{|k| h[k] = hash[k] if hash.has_key?(k)}
59
+ h
60
+ end
61
+
62
+ def env2options(*keys)
63
+ options = {}
64
+ ENV.keys.map do |k|
65
+ key = k.downcase.to_sym
66
+ options[key] = ENV[k] if keys.include?(key)
67
+ end
68
+ options
69
+ end
70
+
71
+ def define_delegation(opts)
72
+ file, line = caller.first.split(':', 2)
73
+ line = line.to_i
74
+
75
+ obj, meth, methods, to = opts[:in], opts[:by], opts[:for], opts[:to]
76
+
77
+ methods.each do |method|
78
+ obj.send meth, <<-method, file, line - 1
79
+ def #{method}(*args, &block) # def method_name(*args, &block)
80
+ if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
81
+ #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
82
+ end # end
83
+ end # end
84
+ method
85
+ end
86
+
87
+ end
88
+
89
+ def class_name_to_type(class_name)
90
+ type = class_name.tr(':', '_')
91
+ type.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
92
+ type.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
93
+ type.downcase!
94
+ type
95
+ end
96
+
97
+ def type_to_class_name(type)
98
+ type.gsub(/__(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
99
+ end
100
+
101
+
102
+ end
103
+ end