flex 0.1.0

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 (56) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +20 -0
  3. data/VERSION +1 -0
  4. data/flex.gemspec +43 -0
  5. data/lib/flex.rb +418 -0
  6. data/lib/flex/api_methods.yml +108 -0
  7. data/lib/flex/class_proxy.rb +12 -0
  8. data/lib/flex/configuration.rb +57 -0
  9. data/lib/flex/errors.rb +42 -0
  10. data/lib/flex/http_clients/patron.rb +27 -0
  11. data/lib/flex/http_clients/rest_client.rb +38 -0
  12. data/lib/flex/loader.rb +116 -0
  13. data/lib/flex/logger.rb +16 -0
  14. data/lib/flex/model.rb +24 -0
  15. data/lib/flex/model/class_proxy.rb +45 -0
  16. data/lib/flex/model/instance_proxy.rb +101 -0
  17. data/lib/flex/model/manager.rb +67 -0
  18. data/lib/flex/rails.rb +12 -0
  19. data/lib/flex/rails/engine.rb +23 -0
  20. data/lib/flex/rails/helper.rb +16 -0
  21. data/lib/flex/related_model.rb +16 -0
  22. data/lib/flex/related_model/class_proxy.rb +23 -0
  23. data/lib/flex/related_model/class_sync.rb +23 -0
  24. data/lib/flex/related_model/instance_proxy.rb +28 -0
  25. data/lib/flex/result.rb +18 -0
  26. data/lib/flex/result/bulk.rb +20 -0
  27. data/lib/flex/result/collection.rb +51 -0
  28. data/lib/flex/result/document.rb +38 -0
  29. data/lib/flex/result/indifferent_access.rb +11 -0
  30. data/lib/flex/result/search.rb +51 -0
  31. data/lib/flex/result/source_document.rb +63 -0
  32. data/lib/flex/result/source_search.rb +32 -0
  33. data/lib/flex/structure/indifferent_access.rb +44 -0
  34. data/lib/flex/structure/mergeable.rb +21 -0
  35. data/lib/flex/tasks.rb +141 -0
  36. data/lib/flex/template.rb +187 -0
  37. data/lib/flex/template/base.rb +29 -0
  38. data/lib/flex/template/info.rb +50 -0
  39. data/lib/flex/template/partial.rb +31 -0
  40. data/lib/flex/template/search.rb +30 -0
  41. data/lib/flex/template/slim_search.rb +13 -0
  42. data/lib/flex/template/tags.rb +46 -0
  43. data/lib/flex/utility_methods.rb +140 -0
  44. data/lib/flex/utils.rb +59 -0
  45. data/lib/flex/variables.rb +11 -0
  46. data/lib/generators/flex/setup/setup_generator.rb +51 -0
  47. data/lib/generators/flex/setup/templates/flex_config.yml +16 -0
  48. data/lib/generators/flex/setup/templates/flex_dir/es.rb.erb +18 -0
  49. data/lib/generators/flex/setup/templates/flex_dir/es.yml.erb +19 -0
  50. data/lib/generators/flex/setup/templates/flex_dir/es_extender.rb.erb +17 -0
  51. data/lib/generators/flex/setup/templates/flex_initializer.rb.erb +44 -0
  52. data/lib/tasks/index.rake +23 -0
  53. data/test/flex.irt +143 -0
  54. data/test/flex/configuration.irt +53 -0
  55. data/test/irt_helper.rb +12 -0
  56. metadata +211 -0
@@ -0,0 +1,108 @@
1
+ # These methods are available as Flex.<method> variable_hash
2
+ # you can get the updated full reference and usage example of these methods
3
+ # by just doing in the console:
4
+ # >> puts Flex.info
5
+
6
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-indices-exists.html
7
+ indices_exists: &exist
8
+ - HEAD
9
+ - /<<index>>
10
+
11
+ # aliased
12
+ exist?: *exist
13
+
14
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html
15
+ create_index: &create
16
+ - PUT
17
+ - /<<index>>
18
+ - settings:
19
+ number_of_shards: <<number_of_shards= 5 >>
20
+ number_of_replicas: <<number_of_replicas= 1 >>
21
+
22
+ # aliased
23
+ put_index: *create
24
+
25
+ post_index:
26
+ - POST
27
+ - /<<index>>
28
+ - settings:
29
+ number_of_shards: <<number_of_shards= 5 >>
30
+ number_of_replicas: <<number_of_replicas= 1 >>
31
+
32
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-get-settings.html
33
+ get_settings:
34
+ - GET
35
+ - /<<index>>/_settings
36
+
37
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-put-mapping.html
38
+ put_mapping:
39
+ - PUT
40
+ - /<<index>>/<<type>>/_mapping
41
+ - <<type>>:
42
+ properties: <<properties>>
43
+
44
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-get-mapping.html
45
+ get_mapping:
46
+ - GET
47
+ - /<<index>>/<<type>>/_mapping
48
+
49
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-delete-mapping.html
50
+ delete_mapping:
51
+ - DELETE
52
+ - /<<index>>/<<type>>
53
+
54
+ # http://www.elasticsearch.org/guide/reference/api/delete.html
55
+ delete_index:
56
+ - DELETE
57
+ - /<<index>>
58
+
59
+ # http://www.elasticsearch.org/guide/reference/api/delete-by-query.html
60
+ delete_by_query:
61
+ - DELETE
62
+ - /<<index>>/<<type>>/_query
63
+ # pass :data variable query if you need
64
+
65
+ # http://www.elasticsearch.org/guide/reference/api/bulk.html
66
+ bulk:
67
+ - POST
68
+ - /_bulk
69
+ - << lines >>
70
+
71
+ # http://www.elasticsearch.org/guide/reference/api/count.html
72
+ count:
73
+ - GET
74
+ - /<<index>>/<<type>>/_count
75
+ # pass :data structure if you need
76
+
77
+ # http://www.elasticsearch.org/guide/reference/api/admin-indices-stats.html
78
+ stats:
79
+ - GET
80
+ - /<<index>>/_stats/<<end_point= ~ >>
81
+
82
+ # You must pass the :data variable
83
+ store: &store
84
+ - PUT
85
+ - /<<index>>/<<type>>/<<id>>
86
+
87
+ # alias for symmetry with post_store
88
+ put_store: *store
89
+
90
+ # id is set by ES; you must pass :data
91
+ post_store:
92
+ - POST
93
+ - /<<index>>/<<type>>
94
+
95
+ remove:
96
+ - DELETE
97
+ - /<<index>>/<<type>>/<<id>>
98
+
99
+ # http://www.elasticsearch.org/guide/reference/api/get.html
100
+ get:
101
+ - GET
102
+ - /<<index>>/<<type>>/<<id>>
103
+
104
+ # http://www.elasticsearch.org/guide/reference/api/multi-get.html
105
+ multi_get:
106
+ - GET
107
+ - /<<index>>/<<type>>/_mget
108
+ - ids: << ids >>
@@ -0,0 +1,12 @@
1
+ module Flex
2
+ class ClassProxy
3
+ attr_reader :host_class
4
+ attr_accessor :variables
5
+
6
+ def initialize(host_class)
7
+ @host_class = host_class
8
+ @variables = Variables.new
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,57 @@
1
+ module Flex
2
+ class Struct < OpenStruct
3
+
4
+ def configure
5
+ yield self
6
+ end
7
+
8
+ end
9
+
10
+ extend self
11
+
12
+ def load_http_client
13
+ if Gem::Specification.respond_to?(:find_all_by_name)
14
+ case
15
+ # terrible way to check whether a gem is available.
16
+ # Gem.available? was just perfect: that's probably the reason it has been deprecated!
17
+ # https://github.com/rubygems/rubygems/issues/176
18
+ when Gem::Specification::find_all_by_name('patron').any?
19
+ require 'patron'
20
+ Flex::HttpClients::Patron
21
+ when Gem::Specification::find_all_by_name('rest-client').any?
22
+ require 'rest-client'
23
+ Flex::HttpClients::RestClient
24
+ end
25
+ else
26
+ case
27
+ when Gem.available?('patron')
28
+ require 'patron'
29
+ Flex::HttpClients::Patron
30
+ when Gem.available?('rest-client')
31
+ require 'rest-client'
32
+ Flex::HttpClients::RestClient
33
+ end
34
+ end
35
+ end
36
+ private :load_http_client
37
+
38
+ Configuration = Struct.new :base_uri => 'http://localhost:9200',
39
+ :result_extenders => [ Flex::Result::Document,
40
+ Flex::Result::SourceDocument,
41
+ Flex::Result::Search,
42
+ Flex::Result::SourceSearch,
43
+ Flex::Result::Bulk ],
44
+ :logger => Logger.new(STDERR),
45
+ :variables => Variables.new( :index => nil,
46
+ :type => nil,
47
+ :no_pruning => %w[match_all] ),
48
+ :flex_models => [],
49
+ :config_file => './config/flex.yml',
50
+ :flex_dir => './flex',
51
+ :http_client => load_http_client,
52
+ :http_client_options => {},
53
+ :debug => true,
54
+ :debug_result => true,
55
+ :debug_to_curl => false,
56
+ :raise_proc => proc{|response| response.status >= 400}
57
+ end
@@ -0,0 +1,42 @@
1
+ module Flex
2
+
3
+ class ArgumentError < ArgumentError; end
4
+ class SourceError < StandardError; end
5
+ class MissingPartialError < StandardError; end
6
+ class DocumentMappingError < StandardError; end
7
+ class MissingIndexEntryError < StandardError; end
8
+ class ExistingIndexError < StandardError; end
9
+ class MissingHttpClientError < StandardError; end
10
+ class MissingParentError < StandardError; end
11
+
12
+ class HttpError < StandardError
13
+
14
+ attr_reader :response
15
+
16
+ def initialize(response, caller_line=nil)
17
+ @response = response
18
+ @caller_line = caller_line
19
+ end
20
+
21
+ def status
22
+ response.status
23
+ end
24
+
25
+ def body
26
+ response.body
27
+ end
28
+
29
+ def to_s
30
+ log = "#{@caller_line}\n" if @caller_line
31
+ "#{log}#{status}: #{body}"
32
+ end
33
+
34
+ def to_hash
35
+ MultiJson.decode response.body
36
+ rescue MultiJson::DecodeError
37
+ {}
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,27 @@
1
+ module Flex
2
+ module HttpClients
3
+ module Patron
4
+ extend self
5
+
6
+ def request(method, path, data=nil, options={})
7
+ options = Utils.deep_merge_hashes(Configuration.http_client_options, options)
8
+ options = options.merge(:data => data) if data
9
+ session.request method.to_s.downcase.to_sym, path, {}, options
10
+ rescue ::Patron::TimeoutError
11
+ session.request method.to_s.downcase.to_sym, path, {}, options
12
+ end
13
+
14
+ private
15
+
16
+ def session
17
+ Thread.current[:flex_patron_session] ||= begin
18
+ sess = ::Patron::Session.new
19
+ sess.headers['User-Agent'] = "flex-#{Flex::VERSION}"
20
+ sess.base_url = Configuration.base_uri
21
+ sess
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ module Flex
2
+ module HttpClients
3
+ module RestClient
4
+ extend self
5
+
6
+ def request(method, path, data=nil, options={})
7
+ options = Configuration.http_client_options.merge options
8
+ url = Configuration.base_uri + path
9
+ args = options.merge( :method => method.to_s.downcase.to_sym,
10
+ :url => url,
11
+ :payload => data )
12
+ response = ::RestClient::Request.new( args ).execute
13
+ extend_response(response, url)
14
+
15
+ rescue ::RestClient::ExceptionWithResponse, ::RestClient::RequestFailed => e
16
+ extend_response(e.response, url)
17
+ end
18
+
19
+ private
20
+
21
+ def extend_response(response, url)
22
+ response.extend ResponseExtension
23
+ response.url = url
24
+ response
25
+ end
26
+
27
+ module ResponseExtension
28
+ attr_accessor :url
29
+
30
+ def status
31
+ code.to_i
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,116 @@
1
+ module Flex
2
+ module Loader
3
+
4
+ extend self
5
+ attr_accessor :host_classes
6
+ @host_classes = []
7
+
8
+ def self.included(base)
9
+ base.class_eval do
10
+ Flex::Loader.host_classes |= [base]
11
+ class << self; attr_reader :flex end
12
+ @flex = Flex::Loader::ClassProxy.new(base)
13
+ end
14
+ end
15
+
16
+ class ClassProxy < Flex::ClassProxy
17
+ attr_reader :templates, :partials
18
+
19
+ include Template::Info
20
+
21
+ def initialize(base)
22
+ super
23
+ @sources = []
24
+ @templates = {}
25
+ @partials = {}
26
+ end
27
+
28
+ # accepts a path to a file or YAML string
29
+ def load_source_for(klass, source, source_vars)
30
+ if source.nil? || source != /\n/
31
+ paths = [ "#{Configuration.flex_dir}/#{source}.yml",
32
+ "#{Configuration.flex_dir}/#{Model::Manager.class_name_to_type(host_class.name)}.yml",
33
+ source ]
34
+ source = paths.find {|p| File.exist?(p)}
35
+ end
36
+ raise ArgumentError, "expected a string (got #{source.inspect})." \
37
+ unless source.is_a?(String)
38
+ @sources << [klass, source, source_vars]
39
+ do_load_source(klass, source, source_vars)
40
+ end
41
+
42
+ # loads a Generic Template source
43
+ def load_source(source=nil, source_vars=nil)
44
+ load_source_for(Template, source, source_vars)
45
+ end
46
+
47
+ # loads a Search Template source
48
+ def load_search_source(source=nil, source_vars=nil)
49
+ load_source_for(Template::Search, source, source_vars)
50
+ end
51
+
52
+ # loads a SlimSearch Template source
53
+ def load_slim_search_source(source=nil, source_vars=nil)
54
+ load_source_for(Template::SlimSearch, source, source_vars)
55
+ end
56
+
57
+ # reloads the sources (useful in the console and used internally)
58
+ def reload!
59
+ @sources.each {|k,s,v| do_load_source(k,s,v)}
60
+ end
61
+
62
+ # adds a template instance and defines the template method in the host class
63
+ def add_template(name, template)
64
+ templates[name] = template
65
+ # no define_singleton_method in 1.8.7
66
+ host_class.instance_eval <<-ruby, __FILE__, __LINE__ + 1
67
+ def #{name}(vars={})
68
+ raise ArgumentError, "#{host_class}.#{name} expects a Hash (got \#{vars.inspect})" unless vars.is_a?(Hash)
69
+ #{host_class.respond_to?(:preprocess_variables) && 'preprocess_variables(vars)'}
70
+ flex.templates[:#{name}].render(vars)
71
+ end
72
+ ruby
73
+ end
74
+
75
+ # http://www.elasticsearch.org/guide/reference/api/multi-search.html
76
+ # request may be a hash with the templates names as keys and the variable hash as value
77
+ # or you can also use an array of arrays.
78
+ # The variables are an hash of variables that will be used to render the msearch template
79
+ def multi_search(requests, variables={})
80
+ lines = requests.map { |name, vars| templates[name].to_msearch(vars) }.join()
81
+ template = Template.new('GET', '/<<index>>/<<type>>/_msearch')
82
+ template.send(:do_render, variables.merge(:data => lines)) do |http_response|
83
+ responses = []
84
+ es_response = MultiJson.decode(http_response.body)
85
+ es_response['responses'].each_with_index do |result, i|
86
+ responses << Result.new(templates[requests[i].first], requests[i].last, http_response, result)
87
+ end
88
+ es_response['responses'] = responses
89
+ def es_response.responses
90
+ self['responses']
91
+ end
92
+ es_response
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def do_load_source(klass, source, source_vars)
99
+ source = Utils.erb_process(source) unless source.match("\n") # skips non-path
100
+ hash = Utils.data_from_source(source)
101
+ hash.delete('ANCHORS')
102
+ hash.each do |name, structure|
103
+ if name.to_s =~ /^_/ # partial
104
+ partial = Template::Partial.new(structure, self)
105
+ partials[name.to_sym] = partial
106
+ else
107
+ template = klass.new(*structure).setup(self, name, source_vars)
108
+ add_template(name.to_sym, template)
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,16 @@
1
+ require 'logger'
2
+
3
+ module Flex
4
+ class Logger < ::Logger
5
+
6
+ def initialize(*)
7
+ super
8
+ self.level = ::Logger::INFO
9
+ self.progname = "FLEX"
10
+ self.formatter = proc do |severity, datetime, progname, msg|
11
+ "#{msg}\n"
12
+ end
13
+ end
14
+
15
+ end
16
+ end
data/lib/flex/model.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Flex
2
+ module Model
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ class << self; attr_reader :flex end
7
+ @flex ||= Flex::Model::ClassProxy.new(base)
8
+ end
9
+ end
10
+
11
+ def flex
12
+ @flex ||= InstanceProxy.new(self)
13
+ end
14
+
15
+ def flex_source
16
+ to_hash.reject {|k| k.to_s =~ /^_*id$/}.to_json
17
+ end
18
+
19
+ def flex_indexable?
20
+ true
21
+ end
22
+
23
+ end
24
+ end