elasticsearch-store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 99d989d5bd8d8dd530d6ed505b9871ccdd25c711
4
+ data.tar.gz: c173fe470403bc659348019f7613ae2c7dc8087d
5
+ SHA512:
6
+ metadata.gz: 70ccb42f0d868fa2f1077b14edf27e38bbbc7386aa2e450065009186d35bd0319f646f552d1033d72d77c40c0aa0126970021da56166f0b375ef19d517bd5396
7
+ data.tar.gz: 06a24205acc45f269ee667a5c93ae331828df733c2c455d2537f9980d48f00b095d2f316e70af017e5dce9b4a1e0c144c747ad98586297bd6087d3aac9c05f01
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ *emfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ LineLength:
2
+ Enabled: false
3
+
4
+ MethodLength:
5
+ Enabled: false
6
+
7
+ Style/StringLiterals:
8
+ Enabled: false
9
+
10
+ Style/SignalException:
11
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.1.1"
4
+ - "2.0.0"
5
+ - "1.9.3"
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+
8
+ gemfile:
9
+ - gemfiles/3.2.gemfile
10
+ - gemfiles/4.0.gemfile
11
+ - gemfiles/4.1.gemfile
12
+
13
+ install: "bundle install"
14
+
15
+ services:
16
+ - elasticsearch
17
+
18
+ script: "bundle exec rake spec"
data/Appraisals ADDED
@@ -0,0 +1,11 @@
1
+ appraise "rails-3.2" do
2
+ gem "activesupport", "~>3.2.14"
3
+ end
4
+
5
+ appraise "rails-4.0" do
6
+ gem "activesupport", "~>4.0.8"
7
+ end
8
+
9
+ appraise "rails-4.1" do
10
+ gem "activesupport", "~>4.1.4"
11
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elasticsearch-store.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael Pearson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Elasticsearch::Store
2
+
3
+ [![Build Status](https://travis-ci.org/mipearson/elasticsearch-store.svg)](https://travis-ci.org/mipearson/elasticsearch-store) [![Code Climate](https://codeclimate.com/github/mipearson/elasticsearch-store/badges/gpa.svg)](https://codeclimate.com/github/mipearson/elasticsearch-store)
4
+
5
+ ElasticSearch-backed Ruby on Rails cache.
6
+
7
+ Experimental - only tested against a single, local ES instance.
8
+
9
+ Compatible with Ruby 1.9.3 & Rails 3.2 and above.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'elasticsearch-store'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/elasticsearch-store/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
8
+ task test: :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elasticsearch/store/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "elasticsearch-store"
8
+ spec.version = Elasticsearch::Store::VERSION
9
+ spec.authors = ["Michael Pearson"]
10
+ spec.email = ["mipearson@gmail.com"]
11
+ spec.summary = %q{ElasticSearch-backed Ruby on Rails cache.}
12
+ spec.description = %q{}
13
+ spec.homepage = "http://github.com/mipearson/elasticsearch-store"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "elasticsearch", ">1.0.0"
22
+ spec.add_dependency "activesupport", ">3.2.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec", ">=3.0.0"
27
+ end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "activesupport", "~>3.2.14"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "activesupport", "~>4.0.8"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "activesupport", "~>4.1.4"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'elasticsearch'
5
+ rescue LoadError => e
6
+ $stderr.puts <<-EOF
7
+ You don't have elasticsearch installed in your application.
8
+ Please add it to your Gemfile and run bundle install.
9
+ EOF
10
+ raise e
11
+ end
12
+
13
+ require 'digest/md5'
14
+ require 'active_support/core_ext/array/extract_options'
15
+ require 'active_support/core_ext/hash/slice'
16
+ require 'active_support/cache'
17
+
18
+ module ActiveSupport
19
+ module Cache
20
+ # A cache store implementation which stores data in ElasticSearch:
21
+ # http://www.elasticsearch.org/
22
+ #
23
+ # This store is experimental and was developed against a non-clustered
24
+ # ElasticSearch environment.
25
+ class ElasticsearchStore < Store
26
+ # Creates a new ElasticstoreSearch object, with the given elasticsearch
27
+ # server addresses. The addresses and the randomize_hosts,
28
+ # retry_on_failure and reload_connections options are passed verbatim
29
+ # to Elasticsearch::Client.new.
30
+ #
31
+ # Defaults to 'localhost:9200' if no addresses are specified.
32
+ #
33
+ # Will create an index named 'cache' unless the :index_name option is
34
+ # specified.
35
+ def initialize(*addresses)
36
+ addresses = addresses.flatten
37
+ options = addresses.extract_options!
38
+
39
+ es_options = options.extract!(:retry_on_failure, :randomize_hosts, :reload_connections)
40
+ es_options.delete_if { |k, v| v.nil? }
41
+ @index_name = options.delete(:index_name) || 'cache'
42
+
43
+ if options[:namespace]
44
+ raise ArgumentError, ":namespace option not yet supported"
45
+ end
46
+
47
+ super(options)
48
+
49
+ addresses = %w(localhost:9200) if addresses.length == 0
50
+ es_options[:addresses] = addresses
51
+
52
+ @client = Elasticsearch::Client.new(es_options)
53
+ @index_created = false
54
+ end
55
+
56
+ # Clear the entire cache in our Elasticsearch index. This method should
57
+ # be used with care when shared cache is being used.
58
+ def clear(_options = nil)
59
+ # TODO: Support namespaces
60
+ @client.delete_by_query index: @index_name, type: 'entry', body: { query: { match_all: {} } }
61
+ end
62
+
63
+ protected
64
+
65
+ def read_entry(key, options) # :nodoc:
66
+ document = @client.get index: @index_name, type: 'entry', fields: %w(_ttl _source), id: key
67
+
68
+ fields = document['fields'] || {}
69
+ source = document['_source'] || {}
70
+ expires_in = (fields['_ttl'].to_f / 1000.0) if fields.key? '_ttl'
71
+
72
+ Entry.new(
73
+ source["value"],
74
+ options.merge(expires_in: expires_in)
75
+ )
76
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
77
+ nil
78
+ end
79
+
80
+ def write_entry(key, entry, _options) # :nodoc:
81
+ request = { value: entry.value }
82
+ if entry.expires_at
83
+ expires_in = (entry.expires_at - Time.now.to_f) * 1000
84
+ # If we somehow manage to get a negative expires, force it to 1ms
85
+ # as ES will crack it otherwise.
86
+ request[:_ttl] = [expires_in, 1].max
87
+ end
88
+
89
+ @client.index index: @index_name, type: 'entry', id: key, body: request
90
+ end
91
+
92
+ def delete_entry(key, _options) # :nodoc:
93
+ @client.delete index: @index_name, type: 'entry', id: key
94
+ end
95
+
96
+ private
97
+
98
+ def create_index
99
+ @index_created = true
100
+
101
+ # TODO: Check index mappings to make sure they match ours, warn if they don't.
102
+ @client.indices.create index: @index_name, body: {
103
+ settings: { index: {
104
+ number_of_replicas: 0
105
+ } },
106
+ mappings: { entry: { properties: {
107
+ _ttl: { enabled: true },
108
+ value: { type: 'string', index: 'no' }
109
+ } } }
110
+ }
111
+ rescue Elasticsearch::Transport::Transport::Errors::BadRequest => e
112
+ raise unless e.message =~ /IndexAlreadyExists/
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,7 @@
1
+ require "elasticsearch/store/version"
2
+
3
+ module Elasticsearch
4
+ module Store
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Elasticsearch
2
+ module Store
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ require 'rspec'
2
+ require_relative '../lib/activesupport/cache/elasticsearch_store'
3
+
4
+ index_name = "cache_test_#{Process.pid}"
5
+
6
+ describe ActiveSupport::Cache::ElasticsearchStore do
7
+
8
+ describe ".new" do
9
+ before do
10
+ allow_any_instance_of(Elasticsearch::Client).to receive(:create_index)
11
+ end
12
+
13
+ it "should pass the addresses and some options straight through to ES::Client" do
14
+ es_opts = { retry_on_failure: true, randomize_hosts: true, reload_connections: true }
15
+ expect(Elasticsearch::Client).to receive(:new).with(es_opts.merge(addresses: %w(a b)))
16
+
17
+ ActiveSupport::Cache::ElasticsearchStore.new('a', 'b', es_opts)
18
+ end
19
+
20
+ it "should default to localhost:9200" do
21
+ expect(Elasticsearch::Client).to receive(:new).with(addresses: %w(localhost:9200))
22
+ ActiveSupport::Cache::ElasticsearchStore.new
23
+ end
24
+
25
+ it "should error if we try to pass in a namespace" do
26
+ expect { ActiveSupport::Cache::ElasticsearchStore.new(namespace: 'derp') }.to raise_error ArgumentError
27
+ end
28
+ end
29
+
30
+ context "with an active store" do
31
+ let(:store) { ActiveSupport::Cache::ElasticsearchStore.new(index_name: index_name) }
32
+ after do
33
+ # rubocop:disable Style/RescueModifier
34
+ Elasticsearch::Client.new.indices.delete index: index_name
35
+ end
36
+
37
+ it "should store and retrive a cache entry" do
38
+ expect(store.read("hello")).to be_nil
39
+ store.write("hello", "sup")
40
+ expect(store.read("hello")).to eq("sup")
41
+ end
42
+
43
+ it "should expire cache entries" do
44
+ store.write("hi", "there", expires_in: 0.5)
45
+ expect(store.read("hi")).to eq("there")
46
+ sleep 1
47
+ expect(store.read("hi")).to be_nil
48
+ end
49
+
50
+ it "should allow clearing the entire cache" do
51
+ store.write("hello", "there")
52
+ store.write("good", "bye")
53
+ store.clear
54
+ expect(store.read('hello')).to be_nil
55
+ expect(store.read('good')).to be_nil
56
+ end
57
+
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch-store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Pearson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: elasticsearch
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
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: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0
83
+ description: ''
84
+ email:
85
+ - mipearson@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rubocop.yml"
92
+ - ".travis.yml"
93
+ - Appraisals
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - elasticsearch-store.gemspec
99
+ - gemfiles/rails_3.2.gemfile
100
+ - gemfiles/rails_4.0.gemfile
101
+ - gemfiles/rails_4.1.gemfile
102
+ - lib/activesupport/cache/elasticsearch_store.rb
103
+ - lib/elasticsearch/store.rb
104
+ - lib/elasticsearch/store/version.rb
105
+ - spec/elasticsearch_store_spec.rb
106
+ homepage: http://github.com/mipearson/elasticsearch-store
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.2.2
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: ElasticSearch-backed Ruby on Rails cache.
130
+ test_files:
131
+ - spec/elasticsearch_store_spec.rb