flex 0.4.2 → 1.0.1

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