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
@@ -1,16 +1,18 @@
1
1
  module Flex
2
- class Result
3
- module Collection
2
+ module Struct
3
+ module Paginable
4
4
 
5
- attr_reader :total_entries, :variables
5
+ attr_accessor :total_entries, :variables
6
6
 
7
7
  def setup(total_entries, variables)
8
8
  @total_entries = total_entries
9
9
  @variables = variables
10
+ self
10
11
  end
11
12
 
12
13
  def per_page
13
- (@variables[:per_page] || @variables[:params] && @variables[:params][:size] || @variables[:size] || 10).to_i
14
+ (@variables[:per_page] || @variables[:limit_value] ||
15
+ @variables[:params] && @variables[:params][:size] || 10).to_i
14
16
  end
15
17
 
16
18
  def total_pages
@@ -18,11 +20,7 @@ module Flex
18
20
  end
19
21
 
20
22
  def current_page
21
- if @variables[:page]
22
- @variables[:page].to_i
23
- else
24
- (per_page + (@variables[:from]||0).to_i) / per_page
25
- end
23
+ (@variables[:page] || @variables[:current_page] || 1).to_i
26
24
  end
27
25
 
28
26
  def previous_page
@@ -33,6 +31,14 @@ module Flex
33
31
  current_page < total_pages ? (current_page + 1) : nil
34
32
  end
35
33
 
34
+ def last_page?
35
+ total_pages == current_page
36
+ end
37
+
38
+ def first_page?
39
+ current_page == 1
40
+ end
41
+
36
42
  def offset
37
43
  per_page * (current_page - 1)
38
44
  end
@@ -48,4 +54,5 @@ module Flex
48
54
 
49
55
  end
50
56
  end
57
+
51
58
  end
@@ -0,0 +1,60 @@
1
+ module Flex
2
+ module Struct
3
+ module Prunable
4
+
5
+ extend self
6
+
7
+ VALUES = [ nil, '', {}, [], false ]
8
+
9
+ class Value
10
+ class << self
11
+ def to_s; '' end
12
+ alias_method :===, :==
13
+ end
14
+ end
15
+
16
+ def prune_blanks(obj)
17
+ prune(obj, *VALUES) || {}
18
+ end
19
+
20
+ # prunes the branch when the leaf is Prunable
21
+ # and compact.flatten the Array values
22
+ # values are the prunable values, like VALUES or Prunable::Value,
23
+ # or any arbitrary value
24
+ def prune(obj, *values)
25
+ case
26
+ when values.include?(obj)
27
+ obj
28
+ when obj.is_a?(::Array)
29
+ return obj if obj.empty?
30
+ ar = []
31
+ obj.each do |i|
32
+ pruned = prune(i, *values)
33
+ next if values.include?(pruned)
34
+ ar << pruned
35
+ end
36
+ a = ar.compact.flatten
37
+ a.empty? ? values.first : a
38
+ when obj.is_a?(::Hash)
39
+ return obj if obj.empty?
40
+ h = {}
41
+ obj.each do |k, v|
42
+ pruned = prune(v, *values)
43
+ next if values.include?(pruned)
44
+ # when a key is prunable merges the value if it is a hash (allows merging of partials)
45
+ if VALUES.include?(k)
46
+ h.merge!(pruned) if pruned.is_a?(::Hash)
47
+ else
48
+ h[k] = pruned
49
+ end
50
+ end
51
+ h.empty? ? values.first : h
52
+ else
53
+ obj
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ Prunable = Struct::Prunable
60
+ end
@@ -0,0 +1,27 @@
1
+ module Flex
2
+ module Struct
3
+ module AsIs end
4
+ module Symbolize
5
+
6
+ def symbolize(obj)
7
+ case obj
8
+ when Flex::Struct::Hash, Flex::Struct::Array, Flex::Struct::AsIs
9
+ obj
10
+ when ::Hash
11
+ h = Struct::Hash.new
12
+ obj.each do |k,v|
13
+ h[k.to_sym] = symbolize(v)
14
+ end
15
+ h
16
+ when ::Array
17
+ a = Struct::Array.new
18
+ obj.each{|i| a << i}
19
+ a
20
+ else
21
+ obj
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -1,10 +1,23 @@
1
1
  module Flex
2
- module Tasks
3
- extend self
2
+ class Tasks
3
+
4
+ attr_reader :options
5
+
6
+ def initialize(overrides={})
7
+ options = Flex::Utils.env2options *default_options.keys
8
+ options[:index] = options[:index].split(',') if options[:index]
9
+ @options = default_options.merge(options).merge(overrides)
10
+ end
11
+
12
+ def default_options
13
+ @default_options ||= { :force => false,
14
+ :index => Conf.variables[:index],
15
+ :config_file => Conf.config_file }
16
+ end
4
17
 
5
18
  def create_indices
6
19
  indices.each do |index|
7
- delete_index(index) if ENV['FORCE']
20
+ delete_index(index) if options[:force]
8
21
  raise ExistingIndexError, "#{index.inspect} already exists. Please use FORCE=1 if you want to delete it first." \
9
22
  if exist?(index)
10
23
  create(index)
@@ -15,96 +28,24 @@ module Flex
15
28
  indices.each { |index| delete_index(index) }
16
29
  end
17
30
 
18
- def import_models
19
- Configuration.http_client_options[:timeout] = ENV['TIMEOUT'].to_i if ENV['TIMEOUT']
20
- Configuration.http_client_options[:timeout] ||= 20
21
- Configuration.debug = !!ENV['FLEX_DEBUG']
22
- batch_size = ENV['BATCH_SIZE'] && ENV['BATCH_SIZE'].to_i || 1000
23
- options = {}
24
- if ENV['IMPORT_OPTIONS']
25
- ENV['IMPORT_OPTIONS'].split('&').each do |pair|
26
- k, v = pair.split('=')
27
- options[k.to_sym] = v
28
- end
29
- end
30
- deleted = []
31
-
32
- models.each do |klass|
33
- index = klass.flex.index
34
-
35
- if ENV['FORCE']
36
- unless deleted.include?(index)
37
- delete_index(index)
38
- deleted << index
39
- puts "#{index} index deleted"
40
- end
41
- end
42
-
43
- unless exist?(index)
44
- create(index)
45
- puts "#{index} index created"
46
- end
47
-
48
- if defined?(Mongoid::Document) && klass.ancestors.include?(Mongoid::Document)
49
- def klass.find_in_batches(options={})
50
- 0.step(count, options[:batch_size]) do |offset|
51
- yield limit(options[:batch_size]).skip(offset).to_a
52
- end
53
- end
54
- end
55
-
56
- unless klass.respond_to?(:find_in_batches)
57
- STDERR.puts "[ERROR] Class #{klass} does not respond to :find_in_batches. Skipped."
58
- next
59
- end
60
-
61
- pbar = ProgBar.new(klass.count, batch_size, "Class #{klass}: ")
62
-
63
- klass.find_in_batches(:batch_size => batch_size) do |array|
64
- opts = {:index => index}.merge(options)
65
- result = Flex.import_collection(array, opts) || next
66
- pbar.process_result(result, array.size)
67
- end
68
-
69
- pbar.finish
70
- end
31
+ def config_hash
32
+ @config_hash ||= ( hash = YAML.load(Utils.erb_process(config_path))
33
+ Utils.delete_allcaps_keys(hash) )
71
34
  end
72
35
 
73
- private
36
+ private
74
37
 
75
38
  def indices
76
- indices = ENV['INDICES'] || struct.keys
77
- indices = eval(indices) if indices.is_a?(String)
78
- indices = [indices] unless indices.is_a?(Array)
79
- indices
39
+ i = options[:index] || config_hash.keys
40
+ i.is_a?(Array) ? i : [i]
80
41
  end
81
42
 
82
43
  def exist?(index)
83
44
  Flex.exist?(:index => index)
84
45
  end
85
46
 
86
- def struct
87
- @struct ||= begin
88
- @indices_yaml = ENV['CONFIG_FILE'] || Flex::Configuration.config_file
89
- raise Errno::ENOENT, "no such file or directory #{@indices_yaml.inspect}. " +
90
- 'Please, use CONFIG_FILE=/path/to/index.yml ' +
91
- 'or set the Flex::Configuration.config_file properly' \
92
- unless File.exist?(@indices_yaml)
93
- Manager.indices(@indices_yaml)
94
- end
95
- end
96
-
97
-
98
- def models
99
- @models ||= begin
100
- mods = ENV['MODELS'] || Flex::Configuration.flex_models
101
- raise AgrumentError, 'no class defined. Please use MODELS=[ClassA,ClassB]' +
102
- 'or set the Flex::Configuration.flex_models properly' \
103
- if mods.nil? || mods.empty?
104
- mods = eval(mods) if mods.is_a?(String)
105
- mods = [mods] unless mods.is_a?(Array)
106
- mods.map{|c| c.is_a?(String) ? eval("::#{c}") : c}
107
- end
47
+ def config_path
48
+ @config_path ||= options[:config_file] || Conf.config_file
108
49
  end
109
50
 
110
51
  def delete_index(index)
@@ -112,9 +53,8 @@ module Flex
112
53
  end
113
54
 
114
55
  def create(index)
115
- raise MissingIndexEntryError, "no #{index.inspect} entry defined in #@indices_yaml" \
116
- unless struct.has_key?(index)
117
- Flex.POST "/#{index}", struct[index]
56
+ config_hash[index] = {} unless config_hash.has_key?(index)
57
+ Flex.POST "/#{index}", config_hash[index]
118
58
  end
119
59
 
120
60
  end
@@ -7,84 +7,85 @@ module Flex
7
7
  # For more details about templates, see the documentation.
8
8
  class Template
9
9
 
10
- include Base
10
+ include Logger
11
+ include Common
11
12
 
12
13
  def self.variables
13
- Variables.new
14
+ Vars.new
14
15
  end
15
16
 
16
- attr_reader :method, :path, :data, :variables, :tags, :partials
17
+ attr_reader :method, :path
17
18
 
18
- def initialize(method, path, data=nil, vars=nil)
19
+ def initialize(method, path, data=nil, *vars)
19
20
  @method = method.to_s.upcase
20
21
  raise ArgumentError, "#@method method not supported" \
21
22
  unless %w[HEAD GET PUT POST DELETE].include?(@method)
22
23
  @path = path =~ /^\// ? path : "/#{path}"
23
- @data = Utils.data_from_source(data)
24
- @instance_vars = vars
24
+ @data = Utils.parse_source(data)
25
+ @instance_vars = Vars.new(*vars)
25
26
  end
26
27
 
27
- def setup(host_flex, name=nil, source_vars=nil)
28
- @host_flex = host_flex
29
- @name = name
30
- @source_vars = source_vars
31
- self
32
- end
33
-
34
- def render(vars={})
35
- do_render(vars) do |response, int|
36
- Result.new(self, int[:vars], response)
28
+ def render(*vars)
29
+ do_render(*vars) do |response, int|
30
+ Result.new(self, int[:vars], response).to_flex_result
37
31
  end
38
32
  end
39
33
 
40
- def to_a(vars={})
41
- int = interpolate(vars)
42
- a = [method, int[:path], int[:data], @instance_vars]
43
- 2.times { a.pop if a.last.nil? }
34
+ def to_a(*vars)
35
+ vars = Vars.new(*vars)
36
+ int = interpolate(vars)
37
+ a = [method, int[:path], Utils.keyfy(:to_s, int[:data]), Utils.keyfy(:to_s, @instance_vars)]
38
+ 2.times { a.pop if a.last.nil? || a.last.empty? }
44
39
  a
45
40
  end
46
41
 
47
- def to_curl(vars={})
48
- to_curl_string interpolate(vars, strict=true)
42
+ def to_source
43
+ {@name.to_s => to_a}.to_yaml
49
44
  end
50
45
 
51
- def to_flex(name=nil)
52
- (name ? {name.to_s => to_a} : to_a).to_yaml
53
- end
54
46
 
55
47
  private
56
48
 
57
- def do_render(vars={})
58
- int = interpolate(vars, strict=true)
59
- path = build_path(int, vars)
60
- encoded_data = build_data(int, vars)
61
- response = Configuration.http_client.request(method, path, encoded_data)
62
-
63
- # used in Flex.exist?
64
- return response.status == 200 if method == 'HEAD'
65
-
66
- if Configuration.raise_proc.call(response)
49
+ def do_render(*vars)
50
+ vars = Vars.new(*vars)
51
+ int, path, encoded_data, response = try_clean_and_retry(vars)
52
+ return response.status == 200 if method == 'HEAD' # used in Flex.exist?
53
+ if Conf.http_client.raise_proc.call(response)
67
54
  int[:vars][:raise].is_a?(FalseClass) ? return : raise(HttpError.new(response, caller_line))
68
55
  end
69
-
70
56
  result = yield(response, int)
71
-
72
- rescue NameError => e
73
- if e.name == :request
74
- raise MissingHttpClientError,
75
- 'you should install the gem "patron" (recommended for performances) or "rest-client", ' +
76
- 'or provide your own http-client interface and set Flex::Configuration.http_client'
77
- else
78
- raise
79
- end
80
57
  ensure
81
- to_logger(path, encoded_data, result) if int && Configuration.debug && Configuration.logger.level == 0
58
+ log_render(int, path, encoded_data, result)
59
+ result
60
+ end
61
+
62
+ # This allows to use Lucene style search language in the :cleanable_query declared variable and
63
+ # in case of a syntax error it will remove all the problematic characters and retry with a cleaned query_string
64
+ # http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/queryparsersyntax.html
65
+ def try_clean_and_retry(vars)
66
+ response_vars = request(vars)
67
+ if !Prunable::VALUES.include?(vars[:cleanable_query]) && Conf.http_client.raise_proc.call(response_vars[3])
68
+ e = HttpError.new(response_vars[3], caller_line)
69
+ e.to_hash['error'] =~ /^SearchPhaseExecutionException/
70
+ (vars[:cleanable_query].is_a?(String) ? vars[:cleanable_query] : vars[:cleanable_query][:query]).tr!('"&|!(){}[]~^:+-\\', '')
71
+ request vars
72
+ else
73
+ response_vars
74
+ end
75
+ end
76
+
77
+ def request(vars)
78
+ int = interpolate(vars, strict=true)
79
+ path = build_path(int, vars)
80
+ encoded_data = build_data(int, vars)
81
+ response = Conf.http_client.request(method, path, encoded_data)
82
+ return int, path, encoded_data, response
82
83
  end
83
84
 
84
85
  def build_path(int, vars)
85
86
  params = int[:vars][:params]
86
87
  path = vars[:path] || int[:path]
87
- if params
88
+ unless params.empty?
88
89
  path << ((path =~ /\?/) ? '&' : '?')
89
90
  path << params.map { |p| p.join('=') }.join('&')
90
91
  end
@@ -92,60 +93,25 @@ module Flex
92
93
  end
93
94
 
94
95
  def build_data(int, vars)
95
- data = vars[:data] && Utils.data_from_source(vars[:data]) || int[:data]
96
+ data = vars[:data] && Utils.parse_source(vars[:data]) || int[:data]
96
97
  (data.nil? || data.is_a?(String)) ? data : MultiJson.encode(data)
97
98
  end
98
99
 
99
- def to_logger(path, encoded_data, result)
100
- h = {}
101
- h[:method] = method
102
- h[:path] = path
103
- h[:data] = MultiJson.decode(encoded_data) unless encoded_data.nil?
104
- h[:result] = result if result && Configuration.debug_result
105
- log = Configuration.debug_to_curl ? to_curl_string(h) : Utils.stringified_hash(h).to_yaml
106
- Configuration.logger.debug "[FLEX] Rendered #{caller_line}\n#{log}"
107
- end
108
-
109
- def caller_line
110
- method_name = @host_flex && @name && "#{@host_flex.host_class}.#@name"
111
- line = caller.find{|l| l !~ /#{LIB_PATH}/}
112
- ll = ''
113
- ll << "#{method_name} from " if method_name
114
- ll << "#{line}"
115
- ll
116
- end
117
-
118
- def to_curl_string(h)
119
- pretty = h[:path] =~ /\?/ ? '&pretty=1' : '?pretty=1'
120
- curl = %(curl -X#{method} "#{Configuration.base_uri}#{h[:path]}#{pretty}")
121
- if h[:data]
122
- data = if h[:data].is_a?(String)
123
- h[:data].length > 1024 ? h[:data][0,1024] + '...(truncated display)' : h[:data]
124
- else
125
- MultiJson.encode(h[:data], :pretty => true)
126
- end
127
- curl << %( -d '\n#{data}\n')
128
- end
129
- curl
130
- end
131
-
132
100
  def interpolate(*args)
133
- tags = Tags.new
134
- stringified = tags.stringify(:path => @path, :data => @data)
135
- @partials, @tags = tags.map(&:name).partition{|n| n.to_s =~ /^_/}
136
- @variables = Configuration.variables.deep_dup
137
- @variables.add(self.class.variables)
138
- @variables.add(@host_flex.variables) if @host_flex
139
- @variables.add(@source_vars, @instance_vars, tags.variables)
101
+ tags = Tags.new
102
+ stringified = tags.stringify(:path => @path, :data => @data)
103
+ @partials, @tags = tags.partial_and_tag_names
104
+ @base_variables = Conf.variables.deep_merge(self.class.variables)
105
+ @temp_variables = Vars.new(@source_vars, @instance_vars, tags.variables)
140
106
  instance_eval <<-ruby, __FILE__, __LINE__
141
107
  def interpolate(vars={}, strict=false)
108
+ vars = Vars.new(vars) unless vars.is_a?(Flex::Vars)
142
109
  return {:path => path, :data => data, :vars => vars} if vars.empty? && !strict
143
- sym_vars = {}
144
- vars.each{|k,v| sym_vars[k.to_sym] = v} # so you can pass the rails params hash
145
- merged = @variables.deep_merge sym_vars
146
- vars = process_vars(merged)
147
- obj = #{stringified}
148
- obj = prune(obj)
110
+ context_variables = vars[:context] ? vars[:context].flex.variables : (@host_flex && @host_flex.variables)
111
+ vars = @base_variables.deep_merge(context_variables, @temp_variables, vars).finalize
112
+ vars = interpolate_partials(vars)
113
+ obj = #{stringified}
114
+ obj = Prunable.prune(obj, Prunable::Value)
149
115
  obj[:path].tr_s!('/', '/') # removes empty path segments
150
116
  obj[:vars] = vars
151
117
  obj
@@ -154,31 +120,5 @@ module Flex
154
120
  interpolate(*args)
155
121
  end
156
122
 
157
- # prunes the branch when the leaf is a PrunableObject
158
- # and compact.flatten the Array values
159
- def prune(obj)
160
- case obj
161
- when Array
162
- return if obj.is_a?(PrunableObject)
163
- a = obj.map do |i|
164
- next if i.is_a?(PrunableObject)
165
- prune(i)
166
- end.compact.flatten
167
- a unless a.empty?
168
- when Hash
169
- return if obj.is_a?(PrunableObject)
170
- return obj if obj.empty?
171
- h = {}
172
- obj.each do |k, v|
173
- pruned = prune(v)
174
- next if pruned.is_a?(PrunableObject)
175
- h[k] = pruned
176
- end
177
- h unless h.empty?
178
- else
179
- obj
180
- end
181
- end
182
-
183
123
  end
184
124
  end