airspace 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 68fd92350f1c6274af7a30a46918f1b9037ba9d1b0e63b1a88c284a4715f432d
4
+ data.tar.gz: 8b89696d66ec946c6ee807f86cacd4cdf370057b74f9a06b6cdae1bfbfa2448b
5
+ SHA512:
6
+ metadata.gz: 7603141412de37754499c6b148ba58c3c80ce4434d9b58f7d8d4a6e4d36de07827810cff6317281f4ab90af180d3464bf377202c4755ec62c4e330855f702fa3
7
+ data.tar.gz: d0c7cb11e532003bf867fddddc36759bdadd0fd583430b4a1cdbb12e2c2da510c4263fceb824546b1ed33e37cb6a2f9bb74c626616e9f1864c479f09c4279ace
@@ -0,0 +1,8 @@
1
+ # See http://editorconfig.org/
2
+
3
+ [*]
4
+ trim_trailing_whitespace = true
5
+ indent_style = space
6
+ indent_size = 2
7
+ insert_final_newline = true
8
+ end_of_line = lf
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *.gem
3
+ /tmp
4
+ /coverage
5
+ /vendor
@@ -0,0 +1,11 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+
4
+ Metrics/MethodLength:
5
+ Max: 25
6
+
7
+ Metrics/BlockLength:
8
+ ExcludedMethods: ['it', 'describe', 'context', 'let', 'specify']
9
+
10
+ AllCops:
11
+ TargetRubyVersion: 2.3
@@ -0,0 +1 @@
1
+ 2.6.0
@@ -0,0 +1,22 @@
1
+ env:
2
+ global:
3
+ - CC_TEST_REPORTER_ID=8fdaf7c8198b46ebdb0fc75cc04a85a150af827c5f216e12ab1dc189750a8bff
4
+ services:
5
+ - redis-server
6
+ language: ruby
7
+ rvm:
8
+ # Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
9
+ - 2.3.8
10
+ - 2.4.5
11
+ - 2.5.3
12
+ - 2.6.0
13
+ cache: bundler
14
+ before_script:
15
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
16
+ - chmod +x ./cc-test-reporter
17
+ - ./cc-test-reporter before-build
18
+ script:
19
+ - bundle exec rubocop
20
+ - bundle exec rspec
21
+ after_script:
22
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
@@ -0,0 +1,3 @@
1
+ # 1.0.0 (February 21, 2019)
2
+
3
+ Initial Release.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
@@ -0,0 +1,104 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ airspace (1.0.0)
5
+ redis (>= 3.3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ansi (1.5.0)
11
+ ast (2.4.0)
12
+ coderay (1.1.2)
13
+ database_cleaner (1.7.0)
14
+ diff-lcs (1.3)
15
+ docile (1.3.1)
16
+ ffi (1.10.0)
17
+ formatador (0.2.5)
18
+ guard (2.15.0)
19
+ formatador (>= 0.2.4)
20
+ listen (>= 2.7, < 4.0)
21
+ lumberjack (>= 1.0.12, < 2.0)
22
+ nenv (~> 0.1)
23
+ notiffany (~> 0.0)
24
+ pry (>= 0.9.12)
25
+ shellany (~> 0.0)
26
+ thor (>= 0.18.1)
27
+ guard-compat (1.2.1)
28
+ guard-rspec (4.7.3)
29
+ guard (~> 2.1)
30
+ guard-compat (~> 1.1)
31
+ rspec (>= 2.99.0, < 4.0)
32
+ hirb (0.7.3)
33
+ jaro_winkler (1.5.2)
34
+ json (2.1.0)
35
+ listen (3.1.5)
36
+ rb-fsevent (~> 0.9, >= 0.9.4)
37
+ rb-inotify (~> 0.9, >= 0.9.7)
38
+ ruby_dep (~> 1.2)
39
+ lumberjack (1.0.13)
40
+ method_source (0.9.2)
41
+ nenv (0.3.0)
42
+ notiffany (0.1.1)
43
+ nenv (~> 0.1)
44
+ shellany (~> 0.0)
45
+ parallel (1.13.0)
46
+ parser (2.6.0.0)
47
+ ast (~> 2.4.0)
48
+ powerpack (0.1.2)
49
+ pry (0.12.2)
50
+ coderay (~> 1.1.0)
51
+ method_source (~> 0.9.0)
52
+ rainbow (3.0.0)
53
+ rb-fsevent (0.10.3)
54
+ rb-inotify (0.10.0)
55
+ ffi (~> 1.0)
56
+ redis (4.1.0)
57
+ rspec (3.8.0)
58
+ rspec-core (~> 3.8.0)
59
+ rspec-expectations (~> 3.8.0)
60
+ rspec-mocks (~> 3.8.0)
61
+ rspec-core (3.8.0)
62
+ rspec-support (~> 3.8.0)
63
+ rspec-expectations (3.8.2)
64
+ diff-lcs (>= 1.2.0, < 2.0)
65
+ rspec-support (~> 3.8.0)
66
+ rspec-mocks (3.8.0)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.8.0)
69
+ rspec-support (3.8.0)
70
+ rubocop (0.63.1)
71
+ jaro_winkler (~> 1.5.1)
72
+ parallel (~> 1.10)
73
+ parser (>= 2.5, != 2.5.1.1)
74
+ powerpack (~> 0.1)
75
+ rainbow (>= 2.2.2, < 4.0)
76
+ ruby-progressbar (~> 1.7)
77
+ unicode-display_width (~> 1.4.0)
78
+ ruby-progressbar (1.10.0)
79
+ ruby_dep (1.5.0)
80
+ shellany (0.0.1)
81
+ simplecov (0.16.1)
82
+ docile (~> 1.1)
83
+ json (>= 1.8, < 3)
84
+ simplecov-html (~> 0.10.0)
85
+ simplecov-console (0.4.2)
86
+ ansi
87
+ hirb
88
+ simplecov
89
+ simplecov-html (0.10.2)
90
+ thor (0.20.3)
91
+ unicode-display_width (1.4.1)
92
+
93
+ PLATFORMS
94
+ ruby
95
+
96
+ DEPENDENCIES
97
+ airspace!
98
+ database_cleaner (~> 1.7)
99
+ guard-rspec (~> 4.7)
100
+ pry (~> 0.12)
101
+ rspec (~> 3.8)
102
+ rubocop (~> 0.63.1)
103
+ simplecov (~> 0.16.1)
104
+ simplecov-console (~> 0.4.2)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: 'bundle exec rspec' do
4
+ require 'guard/rspec/dsl'
5
+ dsl = Guard::RSpec::Dsl.new(self)
6
+
7
+ # RSpec files
8
+ rspec = dsl.rspec
9
+ watch(rspec.spec_helper) { rspec.spec_dir }
10
+ watch(rspec.spec_support) { rspec.spec_dir }
11
+ watch(rspec.spec_files)
12
+
13
+ # Ruby files
14
+ ruby = dsl.ruby
15
+ dsl.watch_spec_files_for(ruby.lib_files)
16
+ end
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2019 Blue Marble Payroll, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,220 @@
1
+ # Airspace
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/airspace.svg)](https://badge.fury.io/rb/airspace) [![Build Status](https://travis-ci.org/bluemarblepayroll/airspace.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/airspace) [![Maintainability](https://api.codeclimate.com/v1/badges/d25bfce65b230ee956df/maintainability)](https://codeclimate.com/github/bluemarblepayroll/airspace/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/d25bfce65b230ee956df/test_coverage)](https://codeclimate.com/github/bluemarblepayroll/airspace/test_coverage) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ The general use case for this library is as follows:
6
+
7
+ I have a materialized/immutable dataset that I wish to temporarily store. With this temporary storage, I would like to support some level of server-side paging, but I do not need further querying/sorting.
8
+
9
+ A dataset, in this context, is:
10
+ 1. A collection of generic data (i.e. a Ruby hash or some other object)
11
+ 2. A two-dimensional array of pages and rows.
12
+
13
+ What this means is: if you can compute a final form of your dataset and it is immutable then you can use Airspace to temporarily store it in Redis.
14
+
15
+ ## Installation
16
+
17
+ To install through Rubygems:
18
+
19
+ ````
20
+ gem install install airspace
21
+ ````
22
+
23
+ You can also add this to your Gemfile:
24
+
25
+ ````
26
+ bundle add airspace
27
+ ````
28
+
29
+ ## Examples
30
+
31
+ ### A Basic Example
32
+
33
+ Say we have the following dataset:
34
+
35
+ ```ruby
36
+ dataset = {
37
+ movie_name: 'Avengers',
38
+ rating: 'PG-13',
39
+ release_date: Date.new(2012, 5, 5),
40
+ actors: [
41
+ { id: 1, name: 'Iron Man' },
42
+ { id: 2, name: 'Hulk' },
43
+ { id: 3, name: 'Thor' },
44
+ { id: 4, name: 'Spiderman' },
45
+ { id: 5, name: 'Captain America' }
46
+ ]
47
+ }
48
+ ```
49
+
50
+ We could split this data up and also paginate the actors:
51
+
52
+ ```ruby
53
+ data = dataset.slice(:movie_name, :rating, :release_date)
54
+ pages = dataset[:actors].each_slice(2).to_a
55
+ ```
56
+
57
+ We can split out the movie 'data' from the actors 'pages' and store it:
58
+
59
+ ```ruby
60
+ id = ::Airspace.set(Redis.new, data: data pages: pages)
61
+ ```
62
+
63
+ The #set call will return the unique identifier for the stored dataset, which we can now use to retrieve it back:
64
+
65
+ ```ruby
66
+ reader = ::Airspace.get(Redis.new, id)
67
+ ```
68
+
69
+ The #get call will return an ::Airspace::Reader object that contains the data, metadata (page_count, etc...), and methods to retrieve all or one of the pages. We could access back the data as:
70
+
71
+ ```ruby
72
+ reader.data
73
+ ```
74
+
75
+ or get all the pages:
76
+
77
+ ```ruby
78
+ reader.pages
79
+ ```
80
+
81
+ or just the last page:
82
+
83
+ ```ruby
84
+ reader.page(reader.page_count)
85
+ ```
86
+
87
+ The value here is that the Reader will only load the initial set of data (movie in our case), while the paged data will only load it all if necessary (or one at a time as needed.)
88
+
89
+ If you are done with the dataset you can delete it:
90
+
91
+ ```ruby
92
+ success = ::Airspace.del(Redis.new, id)
93
+ ```
94
+
95
+ ### Customization / Options
96
+
97
+ There are a few options you can leverage:
98
+
99
+ * Explicitly Specified ID: If you do not specify an id then one will be assigned for you (SecureRandom.uuid)
100
+ * Key Prefix: Used for key namespace isolation. For example: [id: 123, prefix: movies] => key: movies:123
101
+ * Custom Serializer: You can pass in your own serializer in case the default methods do not suffice.
102
+ * Automatic Expiration (TTL): By default no expiration is set on keys. This allows you to set one.
103
+ * Pages Per Chunk: By default 5 pages will be stored per chunk. If you think this is not the right balance for your needs you can explicitly pass this in.
104
+
105
+ The most complex option is the custom serializer. Airspace comes with a default serializer (::Airspace::Serializer) that defaults to the standard Ruby JSON library. Here is an example of a custom serializer that will store the data and rows as arrays (instead of hashes):
106
+
107
+ ```ruby
108
+ class CustomSerializer < ::Airspace::Serializer
109
+ def serialize_data(obj)
110
+ obj = obj.map { |k, v| [k.to_sym, v] }.to_h
111
+
112
+ json_serialize(
113
+ [
114
+ obj[:movie_name],
115
+ obj[:release_date].to_s,
116
+ obj[:rating]
117
+ ]
118
+ )
119
+ end
120
+
121
+ def deserialize_data(json)
122
+ array = json_deserialize(json)
123
+
124
+ {
125
+ movie_name: array[0],
126
+ release_date: Date.parse(array[1]),
127
+ rating: array[2]
128
+ }
129
+ end
130
+
131
+ def serialize_row(obj)
132
+ obj = obj.map { |k, v| [k.to_sym, v] }.to_h
133
+
134
+ json_serialize([obj[:id], obj[:name]])
135
+ end
136
+
137
+ def deserialize_row(json)
138
+ array = json_deserialize(json)
139
+
140
+ {
141
+ id: array[0],
142
+ name: array[1]
143
+ }
144
+ end
145
+ end
146
+ ```
147
+
148
+ Here is what utilizing all the options would looks like:
149
+
150
+ ```ruby
151
+ set_options = {
152
+ expires_in_seconds: 60 * 60 * 12, # 12 hours
153
+ prefix: 'movies',
154
+ serializer: CustomSerializer.new,
155
+ pages_per_chunk: 100
156
+ }
157
+ id = ::Airspace.set(Redis.new, data: data pages: pages, options: options)
158
+
159
+ # Cannot use expires_in_seconds or pages_per_chunk
160
+ # as they are values stored alongside of the dataset.
161
+ get_and_del_options = {
162
+ prefix: 'movies',
163
+ serializer: CustomSerializer.new
164
+ }
165
+ reader = ::Airspace.get(Redis.new, id, options: get_options)
166
+
167
+ success = ::Airspace.del(Redis.new, id, options: get_and_del_options)
168
+ ```
169
+
170
+ ## Contributing
171
+
172
+ ### Development Environment Configuration
173
+
174
+ Basic steps to take to get this repository compiling:
175
+
176
+ 1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (check airspace.gemspec for versions supported)
177
+ 2. Install bundler (gem install bundler)
178
+ 3. Clone the repository (git clone git@github.com:bluemarblepayroll/airspace.git)
179
+ 4. Navigate to the root folder (cd airspace)
180
+ 5. Install dependencies (bundle)
181
+
182
+ ### Running Tests
183
+
184
+ To execute the test suite run:
185
+
186
+ ````
187
+ bundle exec rspec spec --format documentation
188
+ ````
189
+
190
+ Alternatively, you can have Guard watch for changes:
191
+
192
+ ````
193
+ bundle exec guard
194
+ ````
195
+
196
+ Also, do not forget to run Rubocop:
197
+
198
+ ````
199
+ bundle exec rubocop
200
+ ````
201
+
202
+ ### Publishing
203
+
204
+ Note: ensure you have proper authorization before trying to publish new versions.
205
+
206
+ After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
207
+
208
+ 1. Merge Pull Request into master
209
+ 2. Update the [version number](https://semver.org/) in lib/airspace/version.rb
210
+ 3. Bundle
211
+ 4. Update CHANGELOG.md
212
+ 5. Commit & Push master to remote and ensure CI builds master successfully
213
+ 6. Build the project locally: `gem build airspace`
214
+ 7. Publish package to NPM: `gem push airspace-X.gem` where X is the version to push
215
+ 8. Tag master with new version: `git tag <version>`
216
+ 9. Push tags remotely: `git push origin --tags`
217
+
218
+ ## License
219
+
220
+ This project is MIT Licensed.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './lib/airspace/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'airspace'
7
+ s.version = Airspace::VERSION
8
+ s.summary = 'Redis Dataset Store'
9
+
10
+ s.description = <<-DESCRIPTION
11
+ This library provides a very simple interface for storing/fetching/paging datasets in Redis.
12
+ DESCRIPTION
13
+
14
+ s.authors = ['Matthew Ruggio']
15
+ s.email = ['mruggio@bluemarblepayroll.com']
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
+ s.homepage = 'https://github.com/bluemarblepayroll/airspace'
20
+ s.license = 'MIT'
21
+
22
+ s.required_ruby_version = '>= 2.3.8'
23
+
24
+ s.add_dependency('redis', '>=3.3.0')
25
+
26
+ s.add_development_dependency('database_cleaner', '~>1.7')
27
+ s.add_development_dependency('guard-rspec', '~>4.7')
28
+ s.add_development_dependency('pry', '~>0.12')
29
+ s.add_development_dependency('rspec', '~> 3.8')
30
+ s.add_development_dependency('rubocop', '~>0.63.1')
31
+ s.add_development_dependency('simplecov', '~>0.16.1')
32
+ s.add_development_dependency('simplecov-console', '~>0.4.2')
33
+ end