flex 0.4.2 → 1.0.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.
Files changed (78) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +46 -7
  3. data/VERSION +1 -1
  4. data/flex.gemspec +13 -19
  5. data/lib/flex.rb +66 -392
  6. data/lib/flex/api_stubs.rb +1268 -0
  7. data/lib/flex/api_templates/cluster_api.yml +94 -0
  8. data/lib/flex/api_templates/core_api.yml +202 -0
  9. data/lib/flex/api_templates/indices_api.yml +304 -0
  10. data/lib/flex/class_proxy/base.rb +20 -6
  11. data/lib/flex/class_proxy/templates.rb +97 -0
  12. data/lib/flex/class_proxy/templates/doc.rb +91 -0
  13. data/lib/flex/class_proxy/templates/search.rb +72 -0
  14. data/lib/flex/configuration.rb +17 -49
  15. data/lib/flex/deprecation.rb +153 -0
  16. data/lib/flex/errors.rb +2 -1
  17. data/lib/flex/http_clients/base.rb +15 -0
  18. data/lib/flex/http_clients/loader.rb +51 -0
  19. data/lib/flex/http_clients/patron.rb +9 -7
  20. data/lib/flex/http_clients/rest_client.rb +6 -8
  21. data/lib/flex/logger.rb +24 -3
  22. data/lib/flex/prog_bar.rb +11 -6
  23. data/lib/flex/rails.rb +1 -13
  24. data/lib/flex/result.rb +8 -2
  25. data/lib/flex/result/document.rb +42 -13
  26. data/lib/flex/result/multi_get.rb +24 -0
  27. data/lib/flex/result/search.rb +1 -24
  28. data/lib/flex/struct/array.rb +25 -0
  29. data/lib/flex/struct/hash.rb +105 -0
  30. data/lib/flex/{result/collection.rb → struct/paginable.rb} +16 -9
  31. data/lib/flex/struct/prunable.rb +60 -0
  32. data/lib/flex/struct/symbolize.rb +27 -0
  33. data/lib/flex/tasks.rb +26 -86
  34. data/lib/flex/template.rb +60 -120
  35. data/lib/flex/template/common.rb +42 -0
  36. data/lib/flex/template/logger.rb +66 -0
  37. data/lib/flex/template/partial.rb +12 -15
  38. data/lib/flex/template/search.rb +6 -6
  39. data/lib/flex/template/slim_search.rb +1 -1
  40. data/lib/flex/template/tags.rb +19 -9
  41. data/lib/flex/templates.rb +20 -0
  42. data/lib/flex/utility_methods.rb +80 -89
  43. data/lib/flex/utils.rb +68 -25
  44. data/lib/flex/variables.rb +55 -4
  45. data/lib/tasks.rake +28 -0
  46. metadata +61 -85
  47. data/bin/flexes +0 -174
  48. data/lib/flex/api_methods.yml +0 -108
  49. data/lib/flex/class_proxy/loader.rb +0 -102
  50. data/lib/flex/class_proxy/model.rb +0 -45
  51. data/lib/flex/class_proxy/model_sync.rb +0 -23
  52. data/lib/flex/class_proxy/related_model.rb +0 -23
  53. data/lib/flex/instance_proxy/base.rb +0 -29
  54. data/lib/flex/instance_proxy/model.rb +0 -102
  55. data/lib/flex/instance_proxy/related_model.rb +0 -7
  56. data/lib/flex/loader.rb +0 -18
  57. data/lib/flex/manager.rb +0 -61
  58. data/lib/flex/model.rb +0 -24
  59. data/lib/flex/rails/engine.rb +0 -23
  60. data/lib/flex/rails/helper.rb +0 -16
  61. data/lib/flex/related_model.rb +0 -16
  62. data/lib/flex/result/indifferent_access.rb +0 -11
  63. data/lib/flex/result/source_document.rb +0 -63
  64. data/lib/flex/result/source_search.rb +0 -32
  65. data/lib/flex/structure/indifferent_access.rb +0 -44
  66. data/lib/flex/structure/mergeable.rb +0 -21
  67. data/lib/flex/template/base.rb +0 -41
  68. data/lib/flex/template/info.rb +0 -68
  69. data/lib/generators/flex/setup/setup_generator.rb +0 -48
  70. data/lib/generators/flex/setup/templates/flex_config.yml +0 -16
  71. data/lib/generators/flex/setup/templates/flex_dir/es.rb.erb +0 -18
  72. data/lib/generators/flex/setup/templates/flex_dir/es.yml.erb +0 -19
  73. data/lib/generators/flex/setup/templates/flex_dir/es_extender.rb.erb +0 -17
  74. data/lib/generators/flex/setup/templates/flex_initializer.rb.erb +0 -44
  75. data/lib/tasks/index.rake +0 -17
  76. data/test/flex.irt +0 -143
  77. data/test/flex/configuration.irt +0 -53
  78. data/test/irt_helper.rb +0 -12
@@ -0,0 +1,42 @@
1
+ module Flex
2
+ class Template
3
+ module Common
4
+
5
+ attr_reader :name, :partials, :tags, :data
6
+
7
+ def setup(host_flex, name=nil, *vars)
8
+ @host_flex = host_flex
9
+ @name = name
10
+ @source_vars = Vars.new(*vars) if is_a?(Flex::Template)
11
+ self
12
+ end
13
+
14
+ def interpolate_partials(vars)
15
+ @partials.each do |name|
16
+ partial_assigned_vars = vars[name]
17
+ next if Prunable::VALUES.include?(partial_assigned_vars)
18
+ vars[name] = case partial_assigned_vars
19
+ when Array
20
+ partial_assigned_vars.map {|v| @host_flex.partials[name].interpolate(vars, v)}
21
+ # other partial name (usable as a case output)
22
+ when Symbol
23
+ @host_flex.partials[partial_assigned_vars].interpolate(vars, vars[partial_assigned_vars])
24
+ # a partial object
25
+ when Template::Partial
26
+ partial_assigned_vars.interpolate(vars, vars)
27
+ # on-the-fly partial creation (an empty string would prune it before)
28
+ when String
29
+ Template::Partial.new(partial_assigned_vars).interpolate(vars, vars)
30
+ # switch to include the partial (a false value would prune it before)
31
+ when TrueClass
32
+ @host_flex.partials[name].interpolate(vars, vars)
33
+ else
34
+ @host_flex.partials[name].interpolate(vars, partial_assigned_vars)
35
+ end
36
+ end
37
+ vars
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ module Flex
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_flex && @name && "#{@host_flex.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?(Flex::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
@@ -2,28 +2,25 @@ module Flex
2
2
  class Template
3
3
  class Partial
4
4
 
5
- include Base
5
+ include Common
6
6
 
7
- def initialize(data, parent)
8
- @data = data
9
- @parent = parent
10
- tags = Tags.new
11
- stringified = tags.stringify(data)
12
- @partials, @tags = tags.map(&:name).partition{|n| n.to_s =~ /^_/}
13
- @variables = tags.variables
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
14
13
  instance_eval <<-ruby, __FILE__, __LINE__
15
- def interpolate(main_vars=Variables.new, vars={})
16
- sym_vars = {}
17
- vars.each{|k,v| sym_vars[k.to_sym] = v} # so you can pass the rails params hash
18
- main_vars.add(@variables, sym_vars)
19
- vars = process_vars(main_vars)
14
+ def interpolate(vars={}, partial_assigned_vars={})
15
+ vars = Vars.new(vars, @tags_variables, partial_assigned_vars)
16
+ vars = interpolate_partials(vars)
20
17
  #{stringified}
21
18
  end
22
19
  ruby
23
20
  end
24
21
 
25
- def to_flex(name=nil)
26
- {name.to_s => @data}.to_yaml
22
+ def to_source
23
+ {@name.to_s => @data}.to_yaml
27
24
  end
28
25
 
29
26
  end
@@ -3,17 +3,17 @@ module Flex
3
3
  class Search < Template
4
4
 
5
5
  def initialize(data, vars=nil)
6
- super('GET', "/<<index>>/<<type>>/_search", data, vars)
6
+ super('GET', '/<<index>>/<<type>>/_search', data, vars)
7
7
  end
8
8
 
9
- def to_a(vars={})
10
- int = interpolate(vars)
11
- a = [int[:data]]
12
- a << @instance_vars unless @instance_vars.nil?
9
+ def to_a(*vars)
10
+ a = super
11
+ 2.times{ a.delete_at 0 }
13
12
  a
14
13
  end
15
14
 
16
- def to_msearch(vars={})
15
+ def to_msearch(*vars)
16
+ vars = Vars.new(*vars)
17
17
  int = interpolate(vars, strict=true)
18
18
  header = {}
19
19
  header[:index] = int[:vars][:index] if int[:vars][:index]
@@ -5,7 +5,7 @@ module Flex
5
5
  # removes the fields param (no _source returned)
6
6
  # the result.loaded_collection, will load the records from the db
7
7
  def self.variables
8
- super.add(:params => {:fields => ''})
8
+ super.deep_merge!(:params => {:fields => ''})
9
9
  end
10
10
 
11
11
  end
@@ -2,11 +2,21 @@ module Flex
2
2
  class Template
3
3
  class Tags < Array
4
4
 
5
- TAG_REGEXP = /<<\s*(\w+)\s*(?:=([^>]*))*>>/
5
+ TAG_REGEXP = /<<\s*([\w\.]+)\s*(?:=([^>]*))*>>/
6
6
 
7
+ # tag variables are the defaults defined with the tag
8
+ # a variable could be optional, and the default could be nil
7
9
  def variables
8
- tag_variables = {}
9
- each { |t| tag_variables[t.name] = t.default if t.default || t.optional }
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
10
20
  tag_variables
11
21
  end
12
22
 
@@ -16,15 +26,19 @@ module Flex
16
26
  match =~ TAG_REGEXP
17
27
  t = Tag.new($1, $2)
18
28
  push t unless find{|i| i.name == t.name}
19
- t.stringify(match !~ /^"/)
29
+ (match !~ /^"/) ? "\#{vars.get_prunable(:'#{t.name}')}" : "vars.get_prunable(:'#{t.name}')"
20
30
  end
21
31
  end
22
32
 
33
+ def partial_and_tag_names
34
+ map(&:name).partition{|n| n.to_s =~ /^_/}
35
+ end
36
+
23
37
  end
24
38
 
25
39
  class Tag
26
40
 
27
- RESERVED = [:path, :data, :params, :page, :no_pruning, :raise]
41
+ RESERVED = [:context, :path, :data, :params, :no_pruning, :raw_result, :raise]
28
42
 
29
43
  attr_reader :optional, :name, :default
30
44
 
@@ -36,10 +50,6 @@ module Flex
36
50
  @default = YAML.load(default) unless default.nil?
37
51
  end
38
52
 
39
- def stringify(in_string)
40
- in_string ? "\#{prunable(:#{name}, vars)}" : "prunable(:#{name}, vars)"
41
- end
42
-
43
53
  end
44
54
 
45
55
  end
@@ -0,0 +1,20 @@
1
+ module Flex
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
+ Flex::Templates.contexts |= [context]
11
+ @flex ||= ClassProxy::Base.new(context)
12
+ @flex.extend(ClassProxy::Templates).init
13
+ def self.flex; @flex end
14
+ def self.template_methods; flex.templates.keys end
15
+ eval "extend module #{context}::FlexTemplateMethods; self end"
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -1,18 +1,13 @@
1
1
  module Flex
2
2
  module UtilityMethods
3
3
 
4
- # Anonymous search query: please, consider to use named templates for better performances and programming style
5
- # data can be a JSON string that will be passed as is, or a YAML string (that will be converted into a ruby hash)
6
- # or a hash. It can contain interpolation tags as usual.
7
- # You can pass an optional hash of interpolation arguments (or query string :params).
8
- # See also the Flex::Template::Search documentation
9
- def search(data, args={})
10
- Template::Search.new(data).render(args)
4
+ def search(data, vars={})
5
+ Template::Search.new(data).setup(Flex.flex).render(vars)
11
6
  end
12
7
 
13
8
  # like Flex.search, but it will use the Flex::Template::SlimSearch instead
14
- def slim_search(data, args={})
15
- Template::SlimSearch.new(data).render(args)
9
+ def slim_search(data, vars={})
10
+ Template::SlimSearch.new(data).setup(Flex.flex).render(vars)
16
11
  end
17
12
 
18
13
  %w[HEAD GET PUT POST DELETE].each do |m|
@@ -31,109 +26,105 @@ module Flex
31
26
  MultiJson.encode(YAML.load(yaml))
32
27
  end
33
28
 
34
- # Flex.process_bulk accepts a :collection of objects, that can be hashes or Models
35
- # you can pass also a :action set to 'index' (default) or 'delete'
36
- # in order to bulk-index or bulk-delete the whole collection
37
- # you can use Flex.bulk if you have an already formatted bulk data-string
38
- def process_bulk(args)
39
- raise ArgumentError, "Array expected as :collection (got #{args[:collection].inspect})" \
40
- unless args[:collection].is_a?(Array)
41
-
42
- index = args[:index] || Configuration.variables[:index]
43
- type = args[:type] || Configuration.variables[:type]
44
- action = args[:action] || 'index'
45
-
46
- meta = {}
47
- [:version, :routing, :percolate, :parent, :timestamp, :ttl].each do |opt|
48
- meta["_#{opt}"] = args[opt] if args[opt]
49
- end
50
- lines = args[:collection].map do |d|
51
- # skips indexing for objects that return nil as the indexed_json or are not flex_indexable?
52
- unless action == 'delete'
53
- next if d.respond_to?(:flex_indexable?) && !d.flex_indexable?
54
- json = get_json(d) || next
55
- end
56
- m = {}
57
- m['_index'] = get_index(d) || index
58
- m['_type'] = get_type(d) || type
59
- m['_id'] = get_id(d) || d # we could pass an array of ids to delete
60
- parent = get_parent(d)
61
- m['_parent'] = parent if parent
62
- routing = get_routing(d)
63
- m['_routing'] = routing if routing
64
- line = MultiJson.encode({action => meta.merge(m)})
65
- line << "\n#{json}" unless action == 'delete'
66
- line
67
- end.compact
68
-
69
- bulk(args.merge(:lines => lines.join("\n") + "\n")) if lines.size > 0
29
+ def reload!
30
+ flex.variables.deep_merge! Conf.variables
31
+ Templates.contexts.each {|c| c.flex.reload!}
32
+ true
70
33
  end
71
34
 
72
- def import_collection(collection, options={})
73
- process_bulk( {:collection => collection,
74
- :action => 'index'}.merge(options) )
75
-
35
+ def doc(*args)
36
+ flex.doc(*args)
76
37
  end
77
38
 
78
- def delete_collection(collection, options={})
79
- process_bulk( {:collection => collection,
80
- :action => 'delete'}.merge(options) )
39
+ def scan_search(*args, &block)
40
+ flex.scan_search(*args, &block)
81
41
  end
82
42
 
83
- private
43
+ def scan_all(*vars, &block)
44
+ flex.scan_search(:match_all, *vars) do |raw_result|
45
+ batch = raw_result['hits']['hits']
46
+ block.call(batch)
47
+ end
48
+ end
84
49
 
85
- def perform(*args)
86
- Template.new(*args).render
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
87
56
  end
88
57
 
89
- def get_index(d)
90
- d.class.flex.index if d.class.respond_to?(:flex)
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
91
64
  end
92
65
 
93
- def get_type(d)
94
- case
95
- when d.respond_to?(:flex) then d.flex.type
96
- when d.respond_to?(:_type) then d._type
97
- when d.is_a?(Hash) then d.delete(:_type) || d.delete('_type') ||
98
- d.delete(:type) || d.delete('type')
99
- when d.respond_to?(:type) then d.type
66
+ # You should use Flex.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)
100
73
  end
74
+ post_bulk_string(:bulk_string => bulk_string) unless bulk_string.empty?
101
75
  end
102
76
 
103
- def get_parent(d)
104
- case
105
- when d.respond_to?(:flex) && d.flex.parent_instance(false) then d.flex.parent_instance.id
106
- when d.respond_to?(:_parent) then d._parent
107
- when d.respond_to?(:parent) then d.parent
108
- when d.is_a?(Hash) then d.delete(:_parent) || d.delete('_parent') ||
109
- d.delete(:parent) || d.delete('parent')
77
+ def build_bulk_string(document, options={})
78
+ case document
79
+ when Hash
80
+ bulk_string_from_hash(document, options)
81
+ when Flex::ModelIndexer, Flex::ActiveModel
82
+ bulk_string_from_flex(document, options)
83
+ else
84
+ raise NotImplementedError, "Unable to convert the document #{document.inspect} to a bulk string."
110
85
  end
111
86
  end
112
87
 
113
- def get_routing(d)
114
- case
115
- when d.respond_to?(:flex) && d.flex.routing(false) then d.flex.routing
116
- when d.respond_to?(:_routing) then d._routing
117
- when d.respond_to?(:routing) then d.routing
118
- when d.is_a?(Hash) then d.delete(:_routing) || d.delete('_routing') ||
119
- d.delete(:routing) || d.delete('routing')
120
- end
88
+ private
89
+
90
+ def perform(*args)
91
+ Template.new(*args).setup(Flex.flex).render
121
92
  end
122
93
 
123
- def get_id(d)
124
- case
125
- when d.is_a?(Hash) then d.delete(:_id) || d.delete('_id') ||
126
- d.delete(:id) || d.delete('id')
127
- when d.respond_to?(:id) then d.id
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
128
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_flex(document, options)
106
+ flex = document.flex
107
+ return '' unless document.flex_indexable?
108
+ meta = { '_index' => flex.index,
109
+ '_type' => flex.type,
110
+ '_id' => flex.id }
111
+ meta['_parent'] = flex.parent if flex.parent
112
+ meta['_routing'] = flex.routing if flex.routing
113
+ source = document.flex_source unless options[:action] == 'delete'
114
+ to_bulk_string(meta, source, options)
129
115
  end
130
116
 
131
- def get_json(d)
132
- case
133
- when d.respond_to?(:flex_source) then d.flex_source
134
- when d.respond_to?(:to_json) then d.to_json
135
- else MultiJson.encode(d)
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"
136
126
  end
127
+ bulk_string
137
128
  end
138
129
 
139
130
  end