lycra 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f6b763be6e3d625f7ce696812f062ea5cb4f872
4
- data.tar.gz: 801867731e2be4d690668c6268358ce26369ae37
3
+ metadata.gz: 3ea417f626bf68c1e6f119212965332911aa6500
4
+ data.tar.gz: 66f08754c51707c08120be29fd4d9560d63683e3
5
5
  SHA512:
6
- metadata.gz: 5cbb169b8c12801e70c6ef6bdf526b90c08044f08279862a08bf835deb31f347c76f041e81ebd138ac45f28842dc331ff39b4ada881bc553fd563ffccb5bf0c4
7
- data.tar.gz: c640a28436b4039b3e01a611e9ba5c5ada95cb15d29a8510fd2a67f6545038f7c2abfcf63b7a5ea4171b137aaa9e197cc541e9be0869e7571f7892475023c694
6
+ metadata.gz: cd1f851885ead6d4fa92fded263ed0d15a8efb7ab1006a52b5b66396cba5accf9770e2bc70124bb834a539e55126dc5fa3c56f177c287a45f5a0be6960f9067a
7
+ data.tar.gz: d0decf59b1e7a67c5b18aab8dab21ac586a5aed6bd9e2dabd874b80723889c44d76af0db32fafb1ed4724a1d7c8a14efa43f479fd06633fd1e60d978c22daf3d
@@ -0,0 +1,15 @@
1
+ module Lycra
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Lycra
4
+
5
+ initializer "lycra.configure_rails_logger" do
6
+ Lycra.configure do |config|
7
+ config.logger = Rails.logger
8
+ end
9
+ end
10
+
11
+ initializer "lycra.elasticsearch.client" do |app|
12
+ Elasticsearch::Model.client = Lycra.client
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,53 @@
1
+ module Lycra
2
+ class DocumentNotFoundError < StandardError
3
+ attr_reader :model
4
+
5
+ def initialize(model=nil)
6
+ @model = model
7
+
8
+ if model.nil? || model.name.nil?
9
+ msg = <<MSG
10
+ You must define a corresponding document class for all models utilizing Lycra::Model. For example, if your model is called BlogPost:
11
+
12
+ # /app/documents/blog_post_document.rb
13
+ class BlogPostDocument < Lycra::Document
14
+ index_name 'blog-posts'
15
+ end
16
+ MSG
17
+ else
18
+ msg = <<MSG
19
+ You must define a corresponding document class for your #{model.name} model. For example:
20
+
21
+ # /app/documents/#{model.name.split('::').map { |n| n.underscore }.join('/')}_document.rb
22
+ class #{model.name}Document < Lycra::Document
23
+ index_name '#{model.name.parameterize.pluralize}'
24
+ end
25
+ MSG
26
+ end
27
+
28
+ super(msg)
29
+ end
30
+ end
31
+
32
+ class UndefinedIndexError < StandardError
33
+ attr_reader :document
34
+
35
+ def initialize(document=nil)
36
+ @document = document
37
+
38
+ if document.nil?
39
+ super("You must define an index_name for your document class. Try: `index_name 'my-searchable-things'`")
40
+ else
41
+ super("You must define an index_name for your #{document.class.name}. Try: `index_name '#{subject_name.underscore.pluralize}'`")
42
+ end
43
+ end
44
+
45
+ def subject_name
46
+ if document.subject.is_a?(Class)
47
+ document.subject.name
48
+ else
49
+ document.subject.class.name
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,62 @@
1
+ module Lycra
2
+ module Model
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ base.send :include, Elasticsearch::Model
6
+ base.send :include, Elasticsearch::Model::Callbacks
7
+
8
+ base.send :lycra_document
9
+
10
+ base.send :delegate, :as_indexed_json, to: :lycra_document
11
+ end
12
+
13
+ def lycra_document
14
+ self.class.lycra_document.new(self)
15
+ end
16
+
17
+ module ClassMethods
18
+ def index_name(idx=nil)
19
+ lycra_document.index_name idx
20
+ end
21
+
22
+ def document_type(doctype=nil)
23
+ lycra_document.document_type doctype
24
+ end
25
+
26
+ def mapping(options={}, &block)
27
+ lycra_document.mapping options, &block
28
+ end
29
+ alias_method :mappings, :mapping
30
+
31
+ def lycra_document(klass=nil)
32
+ if klass.present?
33
+ if klass.respond_to?(:constantize)
34
+ @lycra_document = klass.constantize.new(self)
35
+ else
36
+ @lycra_document = klass.new(self)
37
+ end
38
+ end
39
+
40
+ @lycra_document ||= lycra_document_klass.new(self)
41
+ end
42
+
43
+ def lycra_document_klass
44
+ begin
45
+ return "#{self.name}Document".constantize
46
+ rescue NameError => e
47
+ # noop, we just continue
48
+ end
49
+
50
+ if respond_to?(:base_class) && self.base_class.name != self.name
51
+ begin
52
+ return "#{self.base_class.name}Document".constantize
53
+ rescue NameError => e
54
+ # noop, we just continue
55
+ end
56
+ end
57
+
58
+ raise Lycra::DocumentNotFoundError.new(self)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,15 @@
1
+ module Lycra
2
+ module Search
3
+ class Aggregations < Array
4
+ def to_query
5
+ return {} if empty?
6
+
7
+ # can probably inject here or be a bit more elegant?
8
+ aggregations = {}
9
+ each { |agg| aggregations.merge!(agg) }
10
+ aggregations
11
+ end
12
+ alias_method :to_q, :to_query
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,200 @@
1
+ module Lycra
2
+ module Search
3
+ module Enumerable
4
+ def self.included(base)
5
+ base.send :delegate, :count, :size, :results, :records, :total_count, to: :response
6
+ base.send :delegate, :first, to: :to_a
7
+
8
+ base.send :alias_method, :total, :total_count
9
+ end
10
+
11
+ def result_type
12
+ @result_type ||= :results
13
+ end
14
+
15
+ def as_results!
16
+ @result_type = :results
17
+ self
18
+ end
19
+
20
+ def as_records!
21
+ @result_type = :records
22
+ self
23
+ end
24
+
25
+ def as_results?
26
+ @result_type != :records
27
+ end
28
+
29
+ def as_records?
30
+ @result_type == :records
31
+ end
32
+
33
+ def decorate!
34
+ @decorated = true
35
+ self
36
+ end
37
+ alias_method :decorated!, :decorate!
38
+
39
+ def undecorate!
40
+ @decorated = false
41
+ self
42
+ end
43
+ alias_method :undecorated!, :undecorate!
44
+
45
+ def decorate?
46
+ @decorated == true
47
+ end
48
+
49
+ def undecorate?
50
+ @decorated != true
51
+ end
52
+
53
+ #### Enumeration ####
54
+
55
+ def enumerable_method
56
+ "#{'decorated_' if decorate?}#{result_type}".to_sym
57
+ end
58
+
59
+ def to_ary
60
+ if decorate?
61
+ send(enumerable_method)
62
+ else
63
+ send(enumerable_method).each do |result|
64
+ apply_transformers(result)
65
+ end.to_a
66
+ end
67
+ end
68
+ alias_method :to_a, :to_ary
69
+
70
+ def each(&block)
71
+ if decorate?
72
+ send("each_#{enumerable_method}", &block)
73
+ else
74
+ send(enumerable_method).each do |result|
75
+ apply_transformers(result)
76
+ yield(result) if block_given?
77
+ end
78
+ end
79
+ end
80
+
81
+ def map(&block)
82
+ if decorate?
83
+ send("map_#{enumerable_method}", &block)
84
+ else
85
+ send(enumerable_method).map do |result|
86
+ apply_transformers(result)
87
+ yield(result) if block_given?
88
+ end
89
+ end
90
+ end
91
+
92
+ #### Decoration & Transformations ####
93
+
94
+ def transform(&block)
95
+ (@transformers ||= []) << block
96
+ self
97
+ end
98
+
99
+ def transformers
100
+ @transformers ||= []
101
+ end
102
+
103
+ def apply_transformers(result)
104
+ transformers.each do |transformer|
105
+ transformer.call(result)
106
+ end
107
+ end
108
+
109
+ def each_decorated_results(decorator=nil, &block)
110
+ map_decorated_results(decorator, &block)
111
+ @decorated_results
112
+ end
113
+ alias_method :decorated_results, :each_decorated_results
114
+ alias_method :each_decorated_result, :each_decorated_results
115
+
116
+ def map_decorated_results(decorator=nil, &block)
117
+ mapped = []
118
+ if @decorated_results
119
+ mapped = @decorated_results.map(&block)
120
+ else
121
+ @decorated_results ||= results.map do |result|
122
+ decorated = decorate_result(result, decorator)
123
+
124
+ apply_transformers(decorated)
125
+ mapped << (block_given? ? yield(decorated) : decorated)
126
+
127
+ decorated
128
+ end
129
+ end
130
+ mapped
131
+ end
132
+
133
+ def decorate_result(result, decorator=nil)
134
+ if decorator
135
+ decorator.decorate(result)
136
+ else
137
+ SearchDecorator.decorate(result)
138
+ end
139
+ end
140
+
141
+ def each_decorated_records(decorator=nil, &block)
142
+ map_decorated_records(decorator, &block)
143
+ @decorated_records
144
+ end
145
+ alias_method :decorated_records, :each_decorated_records
146
+ alias_method :each_decorated_record, :each_decorated_records
147
+
148
+ def map_decorated_records(decorator=nil, &block)
149
+ mapped = []
150
+ if @decorated_records
151
+ mapped = @decorated_records.map(&block)
152
+ else
153
+ @decorated_records ||= records.map do |record|
154
+ decorated = decorate_record(record, decorator)
155
+
156
+ apply_transformers(decorated)
157
+ mapped << (block_given? ? yield(decorated) : decorated)
158
+
159
+ decorated
160
+ end
161
+ end
162
+ mapped
163
+ end
164
+
165
+ def decorate_record(record, decorator=nil)
166
+ if decorator
167
+ decorator.decorate(record)
168
+ else
169
+ record.decorate
170
+ end
171
+ end
172
+
173
+ def records_with_hit(&block)
174
+ @records_with_hit ||= records.map_with_hit do |record,hit|
175
+ mash = Hashie::Mash.new(record: record, hit: hit)
176
+ apply_transformers(mash)
177
+ yield(mash) if block_given?
178
+ mash
179
+ end
180
+ end
181
+
182
+ def decorated_with_hit(decorator=nil, &block)
183
+ @decorated_with_hit ||= records.map_with_hit do |record,hit|
184
+ decorated = begin
185
+ if decorator
186
+ Hashie::Mash.new(record: decorator.decorate(record), hit: hit)
187
+ else
188
+ Hashie::Mash.new(record: record.decorate, hit: hit)
189
+ end
190
+ end
191
+
192
+ apply_transformers(decorated)
193
+ yield(decorated) if block_given?
194
+
195
+ decorated
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,43 @@
1
+ module Lycra
2
+ module Search
3
+ class Filters < Array
4
+ def to_query
5
+ return {} if empty?
6
+
7
+ filters = {}
8
+ filters.merge!(must_filters) unless must_filters.empty?
9
+ filters.merge!(must_not_filters) unless must_not_filters.empty?
10
+ {bool: filters}
11
+ end
12
+ alias_method :to_q, :to_query
13
+
14
+ protected
15
+
16
+ def must_filters
17
+ queries = select { |q| q.key?(:must) || !q.key?(:must_not) }
18
+ return {} if queries.empty?
19
+
20
+ matchers = {must: []}
21
+ queries.each do |query|
22
+ if query.key?(:must)
23
+ matchers[:must].concat(query[:must])
24
+ else
25
+ matchers[:must] << query
26
+ end
27
+ end
28
+ matchers
29
+ end
30
+
31
+ def must_not_filters
32
+ queries = select { |q| q.key?(:must_not) }
33
+ return {} if queries.empty?
34
+
35
+ matchers = {must_not: []}
36
+ queries.each do |query|
37
+ matchers[:must_not].concat(query[:must_not])
38
+ end
39
+ matchers
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ module Lycra
2
+ module Search
3
+ module Pagination
4
+ def self.included(base)
5
+ base.send :delegate, :total_pages, :current_page, :limit_value, :offset_value, :last_page?, to: :response
6
+
7
+ base.send :alias_method, :pages, :total_pages
8
+ end
9
+
10
+ def page(pg=nil)
11
+ @response = response.page(pg || 1)
12
+ self
13
+ end
14
+
15
+ def per(pr=nil)
16
+ @response = response.per(pr || Lycra.configuration.per_page)
17
+ self
18
+ end
19
+
20
+ def offset(ofst=nil)
21
+ @response = response.offset(ofst || 0)
22
+ self
23
+ end
24
+
25
+ def limit(lmt=nil)
26
+ @response = response.limit(lmt || Lycra.configuration.per_page)
27
+ self
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ module Lycra
2
+ module Search
3
+ class Query < Array
4
+ def to_query
5
+ return {} if empty? && filters.empty?
6
+
7
+ query_matcher = {}
8
+ query_matcher.merge!(must_matchers) unless must_matchers.empty?
9
+ query_matcher.merge!(should_matchers) unless should_matchers.empty?
10
+ query_filters = {filter: filters.to_query}
11
+ {
12
+ bool: query_matcher.merge(query_filters)
13
+ }
14
+ end
15
+ alias_method :to_q, :to_query
16
+
17
+ def filters
18
+ @filters ||= Lycra::Search::Filters.new
19
+ end
20
+
21
+ def filter(fltr=nil)
22
+ if fltr.present?
23
+ filters << fltr
24
+ end
25
+
26
+ self
27
+ end
28
+
29
+ def refilter(fltr)
30
+ @filters = Lycra::Search::Filters.new
31
+ filter fltr
32
+ end
33
+
34
+ protected
35
+
36
+ def must_matchers
37
+ queries = select { |q| q.key?(:must) || !q.key?(:should) }
38
+ return {} if queries.empty?
39
+
40
+ matchers = {must: []}
41
+ queries.each do |query|
42
+ if query.key?(:must)
43
+ matchers[:must].concat(query[:must])
44
+ else
45
+ matchers[:must] << query
46
+ end
47
+ end
48
+ matchers
49
+ end
50
+
51
+ def should_matchers
52
+ queries = select { |q| q.key?(:should) }
53
+ return {} if queries.empty?
54
+
55
+ matchers = {should: []}
56
+ queries.each do |query|
57
+ matchers[:should].concat(query[:should])
58
+ end
59
+ matchers
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,136 @@
1
+ module Lycra
2
+ module Search
3
+ module Scoping
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ def aggregations
9
+ @aggregations ||= Lycra::Search::Aggregations.new
10
+ end
11
+
12
+ def aggregate(agg=nil)
13
+ aggregations << agg unless agg.nil?
14
+ self
15
+ end
16
+ alias_method :aggregation, :aggregate
17
+
18
+ def reaggregate(agg)
19
+ @aggregations = Lycra::Search::Aggregations.new
20
+ aggregate agg
21
+ end
22
+
23
+ def filters
24
+ @filters ||= Lycra::Search::Filters.new
25
+ end
26
+
27
+ def filter(fltr=nil)
28
+ filters << fltr unless fltr.nil?
29
+ self
30
+ end
31
+
32
+ def refilter(fltr)
33
+ @filters = Lycra::Search::Filters.new
34
+ filter fltr
35
+ end
36
+
37
+ def query_filters
38
+ query.filters
39
+ end
40
+
41
+ def query_filter(fltr=nil)
42
+ query.filter fltr
43
+ self
44
+ end
45
+
46
+ def requery_filter(fltr)
47
+ query.refilter fltr
48
+ self
49
+ end
50
+
51
+ def post_filters
52
+ @post_filters ||= Lycra::Search::Filters.new
53
+ end
54
+
55
+ def post_filter(fltr=nil)
56
+ post_filters << fltr unless fltr.nil?
57
+ self
58
+ end
59
+
60
+ def repost_filter(fltr)
61
+ @post_filters = Lycra::Search::Filters.new
62
+ post_filter fltr
63
+ end
64
+
65
+ def sorter
66
+ @sorter ||= Lycra::Search::Sort.new
67
+ end
68
+
69
+ def sort(srt=nil)
70
+ sorter << srt unless srt.nil?
71
+ self
72
+ end
73
+
74
+ def resort(srt=nil)
75
+ @sorter = Lycra::Search::Sort.new
76
+ sort srt
77
+ end
78
+
79
+ def filter_by(attr, vals)
80
+ return self if vals.nil? || vals.empty?
81
+
82
+ attr_filter = {
83
+ bool: {
84
+ must: [{
85
+ or: vals.map { |val| {term: {attr.to_sym => val}} }
86
+ }]
87
+ }
88
+ }
89
+
90
+ filter(filter << attr_filter)
91
+
92
+ self
93
+ end
94
+
95
+ def where(*args)
96
+ args.extract_options!.each do |attr, vals|
97
+ vals = [vals] unless vals.is_a?(Array)
98
+ filter_by(attr, vals)
99
+ end
100
+
101
+ self
102
+ end
103
+
104
+ def find_by(*args)
105
+ unfiltered.offset(0).limit(1).where(*args).first
106
+ end
107
+
108
+ def find(id)
109
+ find_by(id: id).first
110
+ end
111
+
112
+ module ClassMethods
113
+ def self.extended(base)
114
+ # Generic Shared Scopes
115
+ base.send :scope, :all, -> { offset(0).limit(10_000) }
116
+ base.send :scope, :unfiltered, -> { refilter }
117
+ base.send :scope, :unsorted, -> { resort }
118
+ base.send :scope, :sort_by, -> (field, order=:asc) { sort({field => {order: order}}) }
119
+ base.send :scope, :by_id, -> (*objs) {
120
+ filter_by(:id, [objs].flatten.map { |obj| obj.respond_to?(:id) ? obj.id : obj })
121
+ }
122
+ end
123
+
124
+ def scope(name, block)
125
+ instance_eval do
126
+ define_method name.to_sym do |*args|
127
+ @response = nil
128
+ instance_exec *args, &block
129
+ self
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,8 @@
1
+ module Lycra
2
+ module Search
3
+ class Sort < Array
4
+ alias_method :to_query, :to_a
5
+ alias_method :to_q, :to_query
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,170 @@
1
+ require 'lycra/search/aggregations'
2
+ require 'lycra/search/filters'
3
+ require 'lycra/search/query'
4
+ require 'lycra/search/sort'
5
+ require 'lycra/search/enumerable'
6
+ require 'lycra/search/pagination'
7
+ require 'lycra/search/scoping'
8
+
9
+ module Lycra
10
+ module Search
11
+ def self.included(base)
12
+ base.send :include, Lycra::Search::Enumerable
13
+ base.send :include, Lycra::Search::Pagination
14
+ base.send :include, Lycra::Search::Scoping
15
+ base.send :extend, ClassMethods
16
+ base.send :attr_reader, :term
17
+ end
18
+
19
+ def initialize(term=nil, query: nil, filter: nil, post_filter: nil, sort: nil, models: nil, fields: nil, aggregations: nil, &block)
20
+ @term = term
21
+ @models = models
22
+ @fields = fields
23
+ self.query(block_given? ? instance_eval(&block) : query)
24
+ self.filter(filter)
25
+ self.post_filter(post_filter)
26
+ self.sort(sort)
27
+ self.aggregate(aggregations)
28
+ end
29
+
30
+ def response
31
+ @response ||= search
32
+ end
33
+
34
+ def response!
35
+ @response = search
36
+ end
37
+
38
+ def document_types
39
+ document_types ||= response.search.definition[:type]
40
+ end
41
+
42
+ def entry_name
43
+ if document_types.count == 1
44
+ document_types.first
45
+ elsif document_types.count > 1
46
+ return document_types.map(&:pluralize).to_sentence
47
+ else
48
+ 'result'
49
+ end
50
+ end
51
+
52
+ def search(qry=nil, &block)
53
+ if block_given?
54
+ Elasticsearch::Model.search(instance_eval(&block), models)
55
+ else
56
+ Elasticsearch::Model.search((qry || to_query), models)
57
+ end
58
+ end
59
+
60
+ def query(qry=nil)
61
+ if !qry.nil?
62
+ @query = Lycra::Search::Query[qry]
63
+ self
64
+ else
65
+ @query ||= Lycra::Search::Query[send("#{query_method}_query")]
66
+ end
67
+ end
68
+
69
+ def to_query_hash
70
+ {
71
+ query: query.to_query,
72
+ filter: filters.to_query,
73
+ sort: sorter.to_query,
74
+ post_filter: post_filters.to_query,
75
+ aggregations: aggregations.to_query
76
+ }
77
+ end
78
+ alias_method :to_query, :to_query_hash
79
+ alias_method :to_q, :to_query_hash
80
+
81
+ def match_all_query
82
+ {match_all: {}}
83
+ end
84
+
85
+ def multi_match_query
86
+ return if term.nil?
87
+
88
+ {
89
+ multi_match: {
90
+ query: term,
91
+ type: :best_fields,
92
+ fields: fields,
93
+ tie_breaker: 0.5,
94
+ operator: 'and'
95
+ }
96
+ }
97
+ end
98
+
99
+ def match_phrase_prefix_query
100
+ return if term.nil?
101
+
102
+ field_queries = fields.map do |field|
103
+ {match_phrase_prefix: {field.to_s.gsub(/\^\d\Z/, '').to_sym => term}}
104
+ end
105
+
106
+ {or: field_queries}
107
+ end
108
+
109
+ def query_method(meth=false)
110
+ if meth == false
111
+ @query_method ||= :match_all
112
+ else
113
+ @query = nil
114
+ @query_method = meth
115
+ self
116
+ end
117
+ end
118
+
119
+ def models(mdls=false)
120
+ if mdls != false
121
+ @models = mdls.is_a?(Array) ? mdls : [mdls]
122
+ else
123
+ @models ||= self.class.models
124
+ end
125
+ end
126
+
127
+ def fields(flds=false)
128
+ if flds != false
129
+ @fields = flds.is_a?(Array) ? flds : [flds]
130
+ else
131
+ @fields ||= self.class.fields
132
+ end
133
+ end
134
+
135
+ module ClassMethods
136
+ def inherited(base)
137
+ # Make sure we inherit the parent's class-level instance variables whenever we inherit from the class.
138
+ base.send :instance_variable_set, :@models, models.try(:dup)
139
+ base.send :instance_variable_set, :@fields, fields.try(:dup)
140
+ end
141
+
142
+ def search(term=nil, query: nil, filter: nil, post_filter: nil, sort: nil, models: nil, fields: nil, aggregations: nil, &block)
143
+ new(term, query: query, filter: filter, post_filter: post_filter, sort: sort, models: models, fields: fields, aggregations: aggregations, &block)
144
+ end
145
+
146
+ def singleton
147
+ @singleton ||= search
148
+ end
149
+
150
+ def method_missing(meth, *args, &block)
151
+ return search.send(meth, *args, &block) if singleton.respond_to?(meth)
152
+ super
153
+ end
154
+
155
+ def respond_to_missing?(meth, include_private=false)
156
+ singleton.respond_to?(meth, include_private) || super
157
+ end
158
+
159
+ def fields(*fields)
160
+ @fields = fields unless fields.empty?
161
+ @fields ||= []
162
+ end
163
+
164
+ def models(*models)
165
+ @models = models unless models.empty?
166
+ @models ||= []
167
+ end
168
+ end
169
+ end
170
+ end
data/lib/lycra/version.rb CHANGED
@@ -2,7 +2,7 @@ module Lycra
2
2
  module Version
3
3
  MAJOR = '0'
4
4
  MINOR = '0'
5
- PATCH = '1'
5
+ PATCH = '2'
6
6
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
 
8
8
  class << self
data/lib/lycra.rb CHANGED
@@ -1,15 +1,41 @@
1
+ require 'logger'
1
2
  require 'canfig'
2
- require 'lycra/railtie' if defined?(Rails)
3
+ require 'elasticsearch/model'
4
+ require 'lycra/model'
5
+ require 'lycra/search'
6
+ require 'lycra/engine' if defined?(Rails)
3
7
 
4
8
  module Lycra
5
9
  include Canfig::Module
6
10
 
7
11
  configure do |config|
8
- config.elastic_host = nil # the elasticsearch host to use
9
- config.logger = nil # defaults to STDOUT but will use Rails.logger in a rails environment
12
+ config.elasticsearch_host = ENV['ELASTICSEARCH_HOST'] || 'localhost' # elasticsearch host to use when connecting, defaults to ENV var if set or falls back to localhost
13
+ config.elasticsearch_port = ENV['ELASTICSEARCH_PORT'] || 9200 # elasticsearch port to use when connecting, defaults to ENV var if set or falls back to 9200
14
+ config.elasticsearch_url = ENV['ELASTICSEARCH_URL'] # elasticsearch URL to use when connecting (i.e. 'https://localhost:9200'), this will override host/port if set
15
+ config.page_size = 50 # default number of results to return per-page when searching an index
16
+ config.index_prefix = nil # a prefix to use for index names (i.e. 'my-app' prefix and 'people' index becomes 'my-app-people')
17
+ config.log = false # whether or not the elasticsearch client should perform standard logging
18
+ config.logger = nil # logger use for standard elasticsearch logging, defaults to STDOUT but will use Rails.logger in a rails environment (via Lycra::Engine)
19
+ config.trace = false # whether or not the elasticsearch client should log request/response traces
20
+ config.tracer = nil # logger used when tracing request/response data
21
+
22
+ def elasticsearch_url
23
+ @state[:elasticsearch_url] || "#{self.elasticsearch_host}:#{self.elasticsearch_port}"
24
+ end
25
+
26
+ def logger
27
+ @logger ||= (@state[:logger] || Logger.new(STDOUT))
28
+ end
10
29
  end
11
30
 
12
- def self.logger
13
- configuration.logger || Logger.new(STDOUT)
31
+ def self.client
32
+ @client ||= Elasticsearch::Client.new(
33
+ host: configuration.elasticsearch_url,
34
+ log: configuration.log,
35
+ logger: configuration.logger,
36
+ trace: configuration.trace,
37
+ tracer: configuration.tracer
38
+ )
14
39
  end
40
+
15
41
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Lycra do
4
+ describe '.logger' do
5
+ context 'when configured with a custom logger' do
6
+ let(:logger) do
7
+ Dir.mktmpdir do |dir|
8
+ Logger.new(File.open(File.join(dir, 'lycra.log'), 'w'))
9
+ end
10
+ end
11
+
12
+ before do
13
+ Lycra.configuration.configure(logger: logger)
14
+ Lycra.configuration.instance_variable_set(:@logger, nil)
15
+ end
16
+
17
+ it 'uses the custom logger' do
18
+ expect(Lycra.configuration.logger).to eq(logger)
19
+ end
20
+ end
21
+
22
+ context 'with default configuration' do
23
+ before do
24
+ Lycra.configuration.configure(logger: nil)
25
+ Lycra.configuration.instance_variable_set(:@logger, nil)
26
+ end
27
+
28
+ it 'is an instance of Logger' do
29
+ expect(Lycra.configuration.logger).to be_an_instance_of(::Logger)
30
+ end
31
+
32
+ it 'uses STDOUT' do
33
+ expect(Lycra.configuration.logger.instance_variable_get(:@logdev).dev).to eq(STDOUT)
34
+ end
35
+ end
36
+ end
37
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,23 +1,21 @@
1
+ require 'faker'
2
+ require 'factory_girl'
1
3
  require 'lycra'
2
4
  require 'rspec'
3
- #require 'vcr'
4
5
  require 'coveralls'
5
6
  Coveralls.wear!
6
7
 
7
8
  #Dir[File.join(File.dirname(__FILE__), '..', "spec/support/**/*.rb")].each { |f| require f }
9
+ Dir[File.join(File.dirname(__FILE__), '..', "spec/factories/**/*.rb")].each { |f| require f }
8
10
 
9
- #VCR.configure do |config|
10
- # config.cassette_library_dir = "spec/cassettes"
11
- # config.hook_into :webmock
12
- #end
11
+ #LYCRA_ES1_CLIENT = Elasticsearch::Client.new host: 'localhost', port: 9201
12
+ LYCRA_ES2_CLIENT = Elasticsearch::Client.new host: 'localhost', port: 4500
13
+ Elasticsearch::Model.client = LYCRA_ES2_CLIENT
13
14
 
14
15
  RSpec.configure do |config|
15
- #config.before(:suite) do
16
- # ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
17
- # capture_stdout { load "db/schema.rb" }
18
- # load 'support/models.rb'
19
- #end
20
-
16
+ # configure factory_girl syntax methods
17
+ config.include FactoryGirl::Syntax::Methods
18
+
21
19
  # rspec-expectations config goes here. You can use an alternate
22
20
  # assertion/expectation library such as wrong or the stdlib/minitest
23
21
  # assertions if you prefer.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lycra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rebec
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-24 00:00:00.000000000 Z
11
+ date: 2016-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: canfig
@@ -24,6 +24,62 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: elasticsearch
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.18
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.18
41
+ - !ruby/object:Gem::Dependency
42
+ name: elasticsearch-persistence
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: elasticsearch-model
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: elasticsearch-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
27
83
  - !ruby/object:Gem::Dependency
28
84
  name: rake
29
85
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +94,20 @@ dependencies:
38
94
  - - ">="
39
95
  - !ruby/object:Gem::Version
40
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
41
111
  - !ruby/object:Gem::Dependency
42
112
  name: rspec
43
113
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +122,34 @@ dependencies:
52
122
  - - ">="
53
123
  - !ruby/object:Gem::Version
54
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: factory_girl
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: faker
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 1.6.6
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 1.6.6
55
153
  description: Open source business intelligence based on elasticsearch queries, inspired
56
154
  by https://github.com/ankane/blazer
57
155
  email:
@@ -61,8 +159,19 @@ extensions: []
61
159
  extra_rdoc_files: []
62
160
  files:
63
161
  - lib/lycra.rb
64
- - lib/lycra/railtie.rb
162
+ - lib/lycra/engine.rb
163
+ - lib/lycra/errors.rb
164
+ - lib/lycra/model.rb
165
+ - lib/lycra/search.rb
166
+ - lib/lycra/search/aggregations.rb
167
+ - lib/lycra/search/enumerable.rb
168
+ - lib/lycra/search/filters.rb
169
+ - lib/lycra/search/pagination.rb
170
+ - lib/lycra/search/query.rb
171
+ - lib/lycra/search/scoping.rb
172
+ - lib/lycra/search/sort.rb
65
173
  - lib/lycra/version.rb
174
+ - spec/lycra_spec.rb
66
175
  - spec/spec_helper.rb
67
176
  homepage: http://github.com/markrebec/lycra
68
177
  licenses: []
@@ -88,4 +197,6 @@ signing_key:
88
197
  specification_version: 4
89
198
  summary: Business intelligence based on elasticsearch queries
90
199
  test_files:
200
+ - spec/lycra_spec.rb
91
201
  - spec/spec_helper.rb
202
+ has_rdoc:
data/lib/lycra/railtie.rb DELETED
@@ -1,9 +0,0 @@
1
- module Lycra
2
- class Railtie < Rails::Railtie
3
- initializer "lycra.configure_rails_logger" do
4
- Lycra.configure do |config|
5
- config.logger = Rails.logger
6
- end
7
- end
8
- end
9
- end