lycra 0.0.3 → 0.0.4

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: 126274cca99ee2410f645b376c5996c4f431bba2
4
- data.tar.gz: 447266fffb8f6976dc8ab21955e3c582e9a93c2d
3
+ metadata.gz: 91043f8065e7fcf15adf93772387b25ad5e84259
4
+ data.tar.gz: 8319be6afc7ee3594784357ffb744dc4b9e71d4f
5
5
  SHA512:
6
- metadata.gz: 70daf0ce79d6ce0609018e69469f9f3e766c42bc647673a9a342ca8cef9ffa9cda3ab3d7cb223febd43049eac6168d619ff20fe9608a1f39f743d600668e201a
7
- data.tar.gz: 1918d122e52cf13dd060c501ce80368952b4a97de16a1867ba29ea8b466d7c9cfef878ce1345cdec295b280c51643bdd31ae8282a46f3cfe699c2947ad7c14de
6
+ metadata.gz: 06811b5b68df2ad87ddf8e35ba19873add41e5a4325dfa3615b822235bcaa615849bf75d846583e043b4d4472e0819c3bf553357f7e8afd489ca8cf29df7ca64
7
+ data.tar.gz: e30ee923b27916255f75c8be97dca2f806cbec8bdd2e770fa4a766baa0a555fb635b720f4364cb12faefbbd78c146c7369bc9969c77031d0bec4196525fcd4ac
@@ -0,0 +1,191 @@
1
+ require 'elasticsearch/persistence/model'
2
+
3
+ module Lycra
4
+ # TODO explain how this is used, what it is, why, etc.
5
+
6
+ # TODO add validations for the results of as_indexed_json against the mapped attributes
7
+
8
+ class Document
9
+ attr_reader :subject
10
+
11
+ INDEX_REGEX = /\A#{::Lycra.configuration.index_prefix}-?/
12
+
13
+ class << self
14
+ def inherited(base)
15
+ # Make sure we inherit the parent's class-level instance variables whenever we inherit from the class.
16
+ # TODO add more comments/examples explaining why we clone these vars
17
+ base.send :instance_variable_set, :@lycra_index_name, index_name.try(:dup)
18
+ base.send :instance_variable_set, :@lycra_document_type, document_type.try(:dup)
19
+ base.send :instance_variable_set, :@lycra_attributes, attributes.try(:dup)
20
+ base.send :instance_variable_set, :@lycra_mapping, mapping.try(:dup)
21
+ base.send :instance_variable_set, :@lycra_settings, settings.try(:dup)
22
+ end
23
+
24
+ def document_type(doctype=nil)
25
+ @lycra_document_type = doctype if doctype.present?
26
+ @lycra_document_type
27
+ end
28
+
29
+ def index_name(zindex_name=nil)
30
+ @lycra_index_name = prefixed_index_name(zindex_name) if zindex_name.present?
31
+ @lycra_index_name
32
+ end
33
+
34
+ def attribute(name, type, mappings={})
35
+ attributes[name] = {type: Elasticsearch::Persistence::Model::Utils.lookup_type(type)}.merge(mappings[:mapping] || {})
36
+ end
37
+
38
+ def attributes(attrs=nil)
39
+ @lycra_attributes = attrs if attrs.present?
40
+ @lycra_attributes ||= {}
41
+ end
42
+
43
+ def mapping(map=nil)
44
+ @lycra_mapping = map if map.present?
45
+ @lycra_mapping ||= {}
46
+ end
47
+ alias_method :mappings, :mapping
48
+
49
+ def settings(settings=nil)
50
+ @lycra_settings = settings if settings.present?
51
+ @lycra_settings ||= {}
52
+ end
53
+
54
+ def prefixed_index_name(idx)
55
+ [::Lycra.configuration.index_prefix, idx.to_s.gsub(INDEX_REGEX, '')].compact.join('-')
56
+ end
57
+
58
+ def index_basename
59
+ index_name.to_s.gsub(INDEX_REGEX, '')
60
+ end
61
+
62
+ def import(*models, **opts, &block)
63
+ models = [models].flatten
64
+ document = models.first.lycra_document
65
+ raise ArgumentError, 'All models must use the same index in order to be imported together' unless models.all? { |model| model.index_name == document.index_name }
66
+
67
+ index_name = opts.delete(:index_name) || document.index_name
68
+
69
+ if opts.delete(:force) == true && document.__elasticsearch__.client.indices.exists?(index: index_name)
70
+ # delete the index if it exists and the force-create option was passed
71
+ document.__elasticsearch__.client.indices.delete index: index_name
72
+ end
73
+
74
+ unless document.__elasticsearch__.client.indices.exists?(index: index_name)
75
+ document.__elasticsearch__.client.indices.create index: index_name, update_all_types: true, body: {
76
+ settings: document.settings,
77
+ mappings: models.inject({}) { |mappings, model| mappings.merge!(model.mappings) } # hacky, need to map all the document mappings
78
+ }
79
+ end
80
+
81
+ models.each do |model|
82
+ model.index_name index_name
83
+ model.import **opts, &block
84
+ model.index_name document.index_name
85
+ end
86
+ end
87
+
88
+ def rotate(*models, **opts, &block)
89
+ models = [models].flatten
90
+ document = models.first.lycra_document
91
+ raise ArgumentError, 'All models must use the same index in order to be imported together' unless models.all? { |model| model.index_name == document.index_name }
92
+
93
+ unstamped_alias = document.index_name
94
+ timestamped_index = [unstamped_alias, Time.now.to_i].compact.join('-')
95
+ existing_index = nil
96
+
97
+ import(*models, **opts.merge({index_name: timestamped_index}), &block)
98
+
99
+ if document.__elasticsearch__.client.indices.exists_alias? name: unstamped_alias
100
+ existing_index = document.__elasticsearch__.client.indices.get_alias(name: unstamped_alias).keys.first
101
+ document.__elasticsearch__.client.indices.delete_alias name: unstamped_alias, index: existing_index
102
+ elsif document.__elasticsearch__.client.indices.exists? index: unstamped_alias
103
+ document.__elasticsearch__.client.indices.delete index: unstamped_alias
104
+ end
105
+ document.__elasticsearch__.client.indices.put_alias name: unstamped_alias, index: timestamped_index
106
+ document.__elasticsearch__.client.indices.delete index: existing_index unless existing_index.nil?
107
+ end
108
+ end
109
+
110
+ # NOTE: HEADS UP! Yes, this is an INSTANCE METHOD!
111
+ # It is a shortcut, since this class represents both your model **class** (i.e. MyModel) and your model **records** (i.e. MyModel.find(1)).
112
+ #
113
+ # normal class usage allows for things like:
114
+ #
115
+ # MyDocument.new(MyModel) # interact with elasticsearch at the model level (index names, mappings, etc.)
116
+ # MyDocument.new(MyModel.find(1)) # interact with elasticsearch at the record level (as_indexed_json, also has access to index name, etc.)
117
+ #
118
+ # but with this, we also get:
119
+ #
120
+ # document = MyDocument.new(MyModel) # instantiate a class-level model document
121
+ # # ... do some stuff at the class-level ...
122
+ # document.new(my_model_record) # easily re-use the same document class to decorate your record without needing to know what it was
123
+ # # ... do some stuff with your record ...
124
+ def new(object)
125
+ self.class.new(object)
126
+ end
127
+
128
+ def initialize(subject)
129
+ @subject = subject
130
+
131
+ if subject.is_a?(Class)
132
+ # TODO explain why and/or maybe add some ! methods that are more explicit about what we're doing
133
+ unless self.class.index_name.present?
134
+ raise Lycra::UndefinedIndexError, self
135
+ end
136
+
137
+ index_name(self.class.index_name)
138
+ document_type(self.class.document_type)
139
+ mapping(self.class.mapping)
140
+ settings(self.class.settings)
141
+ end
142
+ end
143
+
144
+ def document_type(doctype=nil)
145
+ subject.__elasticsearch__.document_type doctype
146
+ end
147
+
148
+ def index_name(idx=nil)
149
+ if idx.present?
150
+ subject.__elasticsearch__.index_name self.class.prefixed_index_name(idx)
151
+ else
152
+ subject.__elasticsearch__.index_name
153
+ end
154
+ end
155
+
156
+ def mapping(options={}, &block)
157
+ self.class.attributes.each do |field, opts|
158
+ subject.__elasticsearch__.mapping.indexes field, opts
159
+ end
160
+
161
+ if options.present? || block_given?
162
+ subject.__elasticsearch__.mapping options, &block
163
+ end
164
+
165
+ subject.__elasticsearch__.mapping
166
+ end
167
+ alias_method :mappings, :mapping
168
+
169
+ def settings(settings=nil, &block)
170
+ subject.__elasticsearch__.settings settings, &block if settings.present? || block_given?
171
+ subject.__elasticsearch__.settings
172
+ end
173
+
174
+ def method_missing(meth, *args, &block)
175
+ return subject.send(meth, *args, &block) if respond_to_missing?(meth, true)
176
+ super
177
+ end
178
+
179
+ def respond_to_missing?(meth, include_private=false)
180
+ subject.respond_to?(meth, include_private)
181
+ end
182
+
183
+ def as_json(opts={})
184
+ subject.as_json(opts)
185
+ end
186
+
187
+ def as_indexed_json(opts={})
188
+ as_json(opts)
189
+ end
190
+ end
191
+ 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 = '3'
5
+ PATCH = '4'
6
6
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
 
8
8
  class << self
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lycra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rebec
@@ -158,6 +158,7 @@ executables: []
158
158
  extensions: []
159
159
  extra_rdoc_files: []
160
160
  files:
161
+ - app/documents/lycra/document.rb
161
162
  - lib/lycra.rb
162
163
  - lib/lycra/engine.rb
163
164
  - lib/lycra/errors.rb