activerecord-crate-adapter 0.0.1
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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +153 -0
- data/Rakefile +9 -0
- data/activerecord-crate-adapter.gemspec +30 -0
- data/history.txt +9 -0
- data/lib/active_record/attribute_methods/crate_object.rb +31 -0
- data/lib/active_record/connection_adapters/crate/database_statements.rb +59 -0
- data/lib/active_record/connection_adapters/crate/quoting.rb +22 -0
- data/lib/active_record/connection_adapters/crate/schema_statements.rb +52 -0
- data/lib/active_record/connection_adapters/crate_adapter.rb +210 -0
- data/lib/activerecord-crate-adapter.rb +2 -0
- data/lib/activerecord-crate-adapter/version.rb +3 -0
- data/lib/arel/arel_crate.rb +3 -0
- data/lib/arel/visitors/crate.rb +10 -0
- data/spec/activerecord/connection_adapters/crate/crate_adapter_spec.rb +0 -0
- data/spec/activerecord/connection_adapters/crate/table_definition_spec.rb +26 -0
- data/spec/data_types/array_spec.rb +49 -0
- data/spec/data_types/object_spec.rb +38 -0
- data/spec/dummy/app/models/address.rb +15 -0
- data/spec/dummy/app/models/post.rb +9 -0
- data/spec/dummy/app/models/user.rb +11 -0
- data/spec/models/post_spec.rb +81 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/test_server.rb +72 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 56df652b663e2d0dd2abcc83154b07b1575ee21b
|
4
|
+
data.tar.gz: f9c8908ea5f6f296d05a16f4502ce12f430d8921
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf7d9a0a55a82065e81fbb3f9a1406e8524b81fef1f8803c0b0594eb4136b26a0af7fc20cf44439fca7390f49b42c4a6e7fde56dfc332c7a38baa9c9fb0689ff
|
7
|
+
data.tar.gz: 6d0d522e9680e251477fada311f1a023ce349a507e43e13f668ee1210d2f7173997885e5da312c9eea957c5fddef827a88e35e24cc2c8f2cda25c87e441041ae
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.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
|
+
.idea
|
19
|
+
.rvmrc
|
20
|
+
crate_test_server*
|
21
|
+
*.log
|
22
|
+
log/*
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Christoph Klocker
|
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,153 @@
|
|
1
|
+
# Activerecord::Crate::Adapter
|
2
|
+
|
3
|
+
The [Crate](http://www.crate.io) adapter for ActiveRecord.
|
4
|
+
|
5
|
+
## Work in progress
|
6
|
+
|
7
|
+
I've just started coding the adapter and lots of functionality might still not work. Give it a try
|
8
|
+
and help bei either contributing (fix it) or add an issue.
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'activerecord-crate-adapter', :git => "https://github.com/crate/activerecord-crate-adapter.git"
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install activerecord-crate-adapter
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
When using Rails update your database.yml
|
28
|
+
|
29
|
+
development:
|
30
|
+
adapter: crate
|
31
|
+
host: 127.0.0.1
|
32
|
+
port: 4200
|
33
|
+
|
34
|
+
Crate doesn't come with an autoincrement feature for your model ids. So you need to set
|
35
|
+
it yourself. One way is to use SecureRandom.uuid, if you think there is a better one,
|
36
|
+
please add an issue so we can discuss.
|
37
|
+
|
38
|
+
class Post < ActiveRecord::Base
|
39
|
+
|
40
|
+
before_validation :set_id
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def set_id
|
45
|
+
self.id = SecureRandom.uuid
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
## Special Data Types
|
51
|
+
|
52
|
+
### Array
|
53
|
+
You can simply create Array columns by specifying t.array and passing array_type when you create a migration. You need at least the upcoming
|
54
|
+
release 0.39 of Crate for this functionality.
|
55
|
+
|
56
|
+
t.array :tags, array_type: :string
|
57
|
+
t.array :votes, array_type: :integer
|
58
|
+
t.array :bool_arr, array_type: :boolean
|
59
|
+
|
60
|
+
When you create an object just pass your Array directly
|
61
|
+
|
62
|
+
Post.create!(title: 'Arrays are awesome', tags: %w(hot fresh), votes: [1,2])
|
63
|
+
post = Post.where("'fresh' = ANY (tags)")
|
64
|
+
|
65
|
+
### Object
|
66
|
+
Crate allows you to define nested objects. You need at least the upcoming
|
67
|
+
release 0.39 of Crate for this functionality. I tried to make it as simply as possible to use and reuse existing AR functionality,
|
68
|
+
I therefore ask you to reuse the existing serialize functionality. AR#serialize allows you to define your own serialization
|
69
|
+
mechanism and we simply reuse that for serializing an AR object. To get serialize working simply create a #dump and #load method
|
70
|
+
on the class that creates a literal statement that is then used in the SQL. Read up more in this [commit}(https://github.com/crate/crate/commit/16a3d4b3f23996a327f91cdacef573f7ba946017).
|
71
|
+
|
72
|
+
I tried to make your guys life easier and created a module that does this automatically for you. Simply make all attributes accessible
|
73
|
+
and assign it in the initializer. So a serialized class should look like this:
|
74
|
+
|
75
|
+
require 'active_record/attribute_methods/crate_object'
|
76
|
+
|
77
|
+
class Address
|
78
|
+
attr_accessor :street, :city, :phones, :zip
|
79
|
+
|
80
|
+
include CrateObject
|
81
|
+
|
82
|
+
def initialize(opts)
|
83
|
+
@street = opts[:street]
|
84
|
+
@city = opts[:city]
|
85
|
+
@phones = opts[:phones]
|
86
|
+
@zip = opts[:zip]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
Check out CrateObject module if you need to write your own serializer.
|
92
|
+
|
93
|
+
Then in your model simply use #serialize to have objects working
|
94
|
+
|
95
|
+
class User < ActiveRecord::Base
|
96
|
+
serialize :address, Address
|
97
|
+
end
|
98
|
+
|
99
|
+
Note: I do not plan to support nested objects inside objects.
|
100
|
+
|
101
|
+
#### Object Migrations
|
102
|
+
|
103
|
+
In the migrations you can create an object and specify the object behaviour(strict|dynamic|ignored) and it's schema.
|
104
|
+
|
105
|
+
t.object :address, object_schema_behaviour: :strict,
|
106
|
+
object_schema: {street: :string, city: :string, phones: {array: :string}, zip: :integer}
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
## Migrations
|
111
|
+
|
112
|
+
Currently adding and dropping indices is not support by Crate. Issue [#733](https://github.com/crate/crate/issues/733)
|
113
|
+
|
114
|
+
# not supported by Crate yet
|
115
|
+
add_index :posts, :comment_count
|
116
|
+
remove_index :posts, :comment_count
|
117
|
+
|
118
|
+
|
119
|
+
## Gotchas
|
120
|
+
|
121
|
+
Crate is eventually consistent, that means if you create a record and query for it right away it
|
122
|
+
won't work (except queries for the primary key!). Read more about it [here](https://github.com/crate/crate/blob/master/docs/sql/dml.txt#L569)
|
123
|
+
|
124
|
+
Crate does not support Joins (yet) so joins won't work.
|
125
|
+
|
126
|
+
## Tests
|
127
|
+
|
128
|
+
First run the test instance of crate
|
129
|
+
|
130
|
+
$ ./spec/test_server.rb
|
131
|
+
|
132
|
+
then run the tests
|
133
|
+
|
134
|
+
$ rspec spec
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
This adapter is a work in progress. If you think something is missing, either follow the steps below
|
139
|
+
or log a new issue, so someone else can tackle it.
|
140
|
+
|
141
|
+
1. Fork it ( `http://github.com/crate/activerecord-crate-adapter/fork` )
|
142
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
143
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
144
|
+
4. Add tests
|
145
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
146
|
+
6. Create new Pull Request
|
147
|
+
|
148
|
+
##Maintainer
|
149
|
+
|
150
|
+
* [Christoph Klocker](http://www.vedanova.com), [@corck](http://www.twitter.com/corck)
|
151
|
+
|
152
|
+
##License
|
153
|
+
MIT License. Copyright 2014 Christoph Klocker. [http://vedanova.com](http://vedanova.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'activerecord-crate-adapter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "activerecord-crate-adapter"
|
8
|
+
spec.version = ActiverecordCrateAdapter::VERSION
|
9
|
+
spec.authors = ["Christoph Klocker"]
|
10
|
+
spec.email = ["christoph@vedanova.com"]
|
11
|
+
spec.summary = "ActiveRecord Crate Data Adapter"
|
12
|
+
spec.description = "ActiveRecord adapter for Crate, a shared-nothing, fully searchable, document-oriented cluster data store."
|
13
|
+
spec.homepage = "https://crate.io"
|
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_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "debugger"
|
24
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
25
|
+
|
26
|
+
spec.add_dependency('activerecord', '>= 4.0.0')
|
27
|
+
spec.add_dependency('arel', '>= 4.0.0')
|
28
|
+
spec.add_dependency('crate_ruby', '~> 0.0.5')
|
29
|
+
|
30
|
+
end
|
data/history.txt
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module CrateObject
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def load(object)
|
6
|
+
case object
|
7
|
+
when String
|
8
|
+
object.gsub!('=', ':')
|
9
|
+
object = JSON.parse("{#{object}}")
|
10
|
+
end
|
11
|
+
new(object.symbolize_keys)
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump(object)
|
15
|
+
object ? object.to_literals : nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def to_literals
|
21
|
+
arr = []
|
22
|
+
instance_variables.each do |var|
|
23
|
+
v = instance_variable_get(var)
|
24
|
+
value = v.is_a?(Array) ? v : %Q{"#{v}"}
|
25
|
+
arr << %Q{"#{var.to_s.gsub(/@/, '')}"=#{value}}
|
26
|
+
end
|
27
|
+
arr.join(', ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module DatabaseStatements
|
4
|
+
def exec_query(sql, name = 'SQL', binds = [])
|
5
|
+
result = nil
|
6
|
+
log(sql, name, binds) {
|
7
|
+
result = do_exec_query(sql, name, binds)
|
8
|
+
}
|
9
|
+
fields = result.cols
|
10
|
+
ActiveRecord::Result.new(fields, result.values)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Converts an arel AST to SQL
|
14
|
+
def to_sql(arel, binds = [])
|
15
|
+
if arel.respond_to?(:ast)
|
16
|
+
binds = binds.dup
|
17
|
+
visitor.accept(arel.ast) do
|
18
|
+
quote(*binds.shift.reverse)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
arel
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_exec_query(sql, name, binds)
|
26
|
+
params = []
|
27
|
+
binds.each_with_index do |(column, value), index|
|
28
|
+
ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column)
|
29
|
+
next if ar_column && column.sql_type == 'timestamp'
|
30
|
+
v = value
|
31
|
+
quoted_value = ar_column ? quote(v, column) : quote(v, nil)
|
32
|
+
params << quoted_value
|
33
|
+
end
|
34
|
+
params.each { |p| sql.sub!(/(\?)/, p) }
|
35
|
+
@connection.execute sql
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the statement identifier for the client side cache
|
39
|
+
# of statements
|
40
|
+
def sql_key(sql)
|
41
|
+
sql
|
42
|
+
end
|
43
|
+
|
44
|
+
# Executes an SQL statement, returning a ResultSet object on success
|
45
|
+
# or raising a CrateError exception otherwise.
|
46
|
+
def execute(sql, name = nil)
|
47
|
+
log(sql, name) do
|
48
|
+
@connection.execute(sql)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
def select(sql, name, binds)
|
54
|
+
exec_query(sql, name, binds)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Crate
|
4
|
+
module Quoting
|
5
|
+
# Quotes the column value to help prevent
|
6
|
+
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
|
7
|
+
def quote(value, column = nil)
|
8
|
+
# records are quoted as their primary key
|
9
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
10
|
+
|
11
|
+
if value.is_a?(Array)
|
12
|
+
"#{value}".gsub('"', "'")
|
13
|
+
elsif column.sql_type == 'object'
|
14
|
+
"{ #{value} }"
|
15
|
+
else
|
16
|
+
super(value, column)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Crate
|
4
|
+
class SchemaCreation < AbstractAdapter::SchemaCreation
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def add_column_options!(sql, options)
|
9
|
+
if options[:array] || options[:column].try(:array)
|
10
|
+
sql.gsub!(/(.*)\s(\w+)$/, '\1 array(\2)')
|
11
|
+
end
|
12
|
+
super(sql, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module SchemaStatements
|
18
|
+
def primary_key(table_name)
|
19
|
+
res = @connection.execute("select constraint_name from information_schema.table_constraints
|
20
|
+
where table_name = '#{quote_table_name(table_name)}' and constraint_type = 'PRIMARY_KEY'")
|
21
|
+
res[0].try(:first).try(:first)
|
22
|
+
end
|
23
|
+
|
24
|
+
# overriding as Crate does not support "version primary key" syntax. Need to add the column type.
|
25
|
+
def initialize_schema_migrations_table
|
26
|
+
unless table_exists?('schema_migrations')
|
27
|
+
execute("CREATE TABLE schema_migrations (version string primary key INDEX using plain)")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_index(table_name, column_name, options = {}) #:nodoc:
|
32
|
+
puts
|
33
|
+
puts "#########"
|
34
|
+
puts "Adding indices is currently not supported by Crate"
|
35
|
+
puts "See issue: https://github.com/crate/crate/issues/733"
|
36
|
+
puts "#########"
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_index(table_name, column_name, options = {}) #:nodoc:
|
41
|
+
puts
|
42
|
+
puts "#########"
|
43
|
+
puts "Dropping indices is currently not supported by Crate"
|
44
|
+
puts "See issue: https://github.com/crate/crate/issues/733"
|
45
|
+
puts "#########"
|
46
|
+
puts
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_record/base'
|
3
|
+
require 'active_record/base'
|
4
|
+
require 'arel/arel_crate'
|
5
|
+
require 'arel/visitors/bind_visitor'
|
6
|
+
require 'active_support/dependencies/autoload'
|
7
|
+
require 'active_support/callbacks'
|
8
|
+
require 'active_support/core_ext/string'
|
9
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
10
|
+
require 'active_record/connection_adapters/statement_pool'
|
11
|
+
require 'active_record/connection_adapters/column'
|
12
|
+
require 'active_record/connection_adapters/crate/schema_statements'
|
13
|
+
require 'active_record/connection_adapters/crate/database_statements'
|
14
|
+
require 'active_record/connection_adapters/crate/quoting'
|
15
|
+
require 'active_support/core_ext/kernel'
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'crate_ruby'
|
19
|
+
rescue LoadError => e
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
|
23
|
+
module ActiveRecord
|
24
|
+
|
25
|
+
class Base
|
26
|
+
def self.crate_connection(config) #:nodoc:
|
27
|
+
config = config.symbolize_keys
|
28
|
+
ConnectionAdapters::CrateAdapter.new(nil, logger, nil, config)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ConnectionAdapters
|
33
|
+
class CrateAdapter < AbstractAdapter
|
34
|
+
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
|
35
|
+
attr_accessor :array, :object
|
36
|
+
end
|
37
|
+
|
38
|
+
include Crate::SchemaStatements
|
39
|
+
include DatabaseStatements
|
40
|
+
include Crate::Quoting
|
41
|
+
|
42
|
+
ADAPTER_NAME = 'Crate'.freeze
|
43
|
+
|
44
|
+
def schema_creation # :nodoc:
|
45
|
+
Crate::SchemaCreation.new self
|
46
|
+
end
|
47
|
+
|
48
|
+
NATIVE_DATABASE_TYPES = {
|
49
|
+
boolean: {name: "boolean"},
|
50
|
+
string: {name: "string"},
|
51
|
+
integer: {name: "integer"},
|
52
|
+
float: {name: "float"},
|
53
|
+
binary: {name: "byte"},
|
54
|
+
datetime: {name: "timestamp"},
|
55
|
+
timestamp: {name: "timestamp"},
|
56
|
+
object: {name: "object"},
|
57
|
+
array: {name: "array"}
|
58
|
+
}
|
59
|
+
|
60
|
+
class BindSubstitution < Arel::Visitors::Crate # :nodoc:
|
61
|
+
include Arel::Visitors::BindVisitor
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize(connection, logger, pool, config)
|
65
|
+
@port = config[:port]
|
66
|
+
@host = config[:host]
|
67
|
+
super(connection, logger, pool)
|
68
|
+
@schema_cache = SchemaCache.new self
|
69
|
+
@visitor = Arel::Visitors::Crate.new self
|
70
|
+
@quoted_column_names = {}
|
71
|
+
connect
|
72
|
+
end
|
73
|
+
|
74
|
+
def adapter_name
|
75
|
+
ADAPTER_NAME
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds `:array` option to the default set provided by the
|
79
|
+
# AbstractAdapter
|
80
|
+
def prepare_column_options(column, types)
|
81
|
+
spec = super
|
82
|
+
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
83
|
+
spec
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds `:array` as a valid migration key
|
87
|
+
def migration_keys
|
88
|
+
super + [:array, :object_schema_behaviour, :object_schema_behaviour]
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
#TODO check what call to use for active
|
93
|
+
def active?
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
#TODO
|
98
|
+
def clear_cache!
|
99
|
+
end
|
100
|
+
|
101
|
+
#TODO
|
102
|
+
def reset!
|
103
|
+
end
|
104
|
+
|
105
|
+
def supports_migrations?
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def connect
|
110
|
+
@connection = CrateRuby::Client.new(["#{@host}:#{@port}"])
|
111
|
+
end
|
112
|
+
|
113
|
+
def columns(table_name) #:nodoc:
|
114
|
+
cols = @connection.table_structure(table_name).map do |field|
|
115
|
+
CrateColumn.new(field[2], nil, field[4], nil)
|
116
|
+
end
|
117
|
+
cols
|
118
|
+
end
|
119
|
+
|
120
|
+
def tables
|
121
|
+
@connection.tables
|
122
|
+
end
|
123
|
+
|
124
|
+
# def quote_column_name(name) #:nodoc:
|
125
|
+
# @quoted_column_names[name] ||= %Q{"#{name.to_s}"}
|
126
|
+
# end
|
127
|
+
|
128
|
+
class CrateColumn < Column
|
129
|
+
|
130
|
+
def simplified_type(field_type)
|
131
|
+
case field_type
|
132
|
+
when /_array/i
|
133
|
+
:array
|
134
|
+
when /object/i
|
135
|
+
:object
|
136
|
+
else
|
137
|
+
super(field_type)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
144
|
+
|
145
|
+
# Crate doesn't support auto incrementing, therefore we need to manually
|
146
|
+
# set a primary key. You need to assure that you always provide an unique
|
147
|
+
# id. This might be done via the
|
148
|
+
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
149
|
+
def primary_key(name, type = :primary_key, options = {})
|
150
|
+
options[:primary_key] = true
|
151
|
+
column name, "STRING PRIMARY KEY", options
|
152
|
+
end
|
153
|
+
|
154
|
+
def column(name, type = nil, options = {})
|
155
|
+
super
|
156
|
+
column = self[name]
|
157
|
+
column.array = options[:array]
|
158
|
+
column.object = options[:object]
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
def object(name, options = {})
|
163
|
+
schema_behaviour = options.delete(:object_schema_behaviour)
|
164
|
+
type = schema_behaviour ? "object(#{schema_behaviour})" : schema_behaviour
|
165
|
+
schema = options.delete(:object_schema)
|
166
|
+
type = "#{type} as (#{object_schema_to_string(schema)})" if schema
|
167
|
+
|
168
|
+
column name, type, options.merge(object: true)
|
169
|
+
end
|
170
|
+
|
171
|
+
def array(name, options = {})
|
172
|
+
array_type = options.delete(:array_type)
|
173
|
+
raise "Array columns must specify an :array_type (e.g. array_type: :string)" unless array_type.present?
|
174
|
+
column name, "array(#{array_type})", options.merge(array: true)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def create_column_definition(name, type)
|
180
|
+
ColumnDefinition.new name, type
|
181
|
+
end
|
182
|
+
|
183
|
+
def object_schema_to_string(s)
|
184
|
+
ary = []
|
185
|
+
s.each_pair do |k, v|
|
186
|
+
if v.is_a?(Symbol)
|
187
|
+
ary << "#{k} #{v}"
|
188
|
+
elsif v.is_a?(Hash)
|
189
|
+
a = "array(#{v[:array]})"
|
190
|
+
ary << "#{k} #{a}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
ary.join(', ')
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_table_definition(name, temporary, options, as = nil)
|
200
|
+
TableDefinition.new native_database_types, name, temporary, options, as
|
201
|
+
end
|
202
|
+
|
203
|
+
def native_database_types
|
204
|
+
NATIVE_DATABASE_TYPES
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
end
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../../../spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::ConnectionAdapters::CrateAdapter::TableDefinition do
|
4
|
+
|
5
|
+
|
6
|
+
describe '#object_schema_to_string' do
|
7
|
+
|
8
|
+
let(:td) { ActiveRecord::ConnectionAdapters::CrateAdapter::TableDefinition.new(nil, nil, nil, nil) }
|
9
|
+
|
10
|
+
it 'should simply set the key and values' do
|
11
|
+
s = {street: :string, city: :string}
|
12
|
+
str = td.send(:object_schema_to_string, s)
|
13
|
+
str.should eq "street string, city string"
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should simply properly parse an array definition' do
|
17
|
+
s = {street: :string, city: :string, phones: {array: :string}}
|
18
|
+
str = td.send(:object_schema_to_string, s)
|
19
|
+
str.should eq "street string, city string, phones array(string)"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe "Post#array" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Migration.class_eval do
|
7
|
+
create_table :posts do |t|
|
8
|
+
t.string :title
|
9
|
+
t.integer :comment_count
|
10
|
+
t.array :tags, array_type: :string
|
11
|
+
t.array :votes, array_type: :integer
|
12
|
+
t.array :bool_arr, array_type: :boolean
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Post.reset_column_information
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:all) do
|
19
|
+
ActiveRecord::Migration.class_eval do
|
20
|
+
drop_table :posts
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
describe "#array column type" do
|
26
|
+
let(:array) {%w(hot fresh)}
|
27
|
+
let(:votes) {[9,8,7]}
|
28
|
+
let(:bool_arr) {[true, false, true]}
|
29
|
+
let(:post) {Post.create!(title: 'Arrays are awesome', tags: array, votes: votes, bool_arr: bool_arr )}
|
30
|
+
|
31
|
+
it 'should store and return an array' do
|
32
|
+
p = Post.find(post.id)
|
33
|
+
p.tags.should be_a Array
|
34
|
+
p.votes.should be_a Array
|
35
|
+
p.bool_arr.should be_a Array
|
36
|
+
p.tags.should eq array
|
37
|
+
p.votes.should eq votes
|
38
|
+
p.bool_arr.should eq bool_arr
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should find the post by array value' do
|
42
|
+
post = Post.create!(title: 'Arrays are awesome', tags: array, votes: votes)
|
43
|
+
refresh_posts
|
44
|
+
Post.where("'fresh' = ANY (tags)").should include(post)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe "User#object" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Migration.class_eval do
|
7
|
+
create_table :users do |t|
|
8
|
+
t.string :name
|
9
|
+
t.object :address, object_schema_behaviour: :strict,
|
10
|
+
object_schema: {street: :string, city: :string, phones: {array: :string}, zip: :integer}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
User.reset_column_information
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
ActiveRecord::Migration.class_eval do
|
18
|
+
drop_table :users
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
describe "#object column type" do
|
24
|
+
let(:address) {Address.new(street: '1010 W 2nd Ave', city: 'Vancouver', phones: ["123", "987"], zip: 6888)}
|
25
|
+
let(:user) {@user = User.create!(name: 'Mad Max', address: address)}
|
26
|
+
|
27
|
+
it 'should store and return an object' do
|
28
|
+
p = User.find(user.id)
|
29
|
+
p.address.should be_a Address
|
30
|
+
p.address.street.should eq address.street
|
31
|
+
p.address.city.should eq address.city
|
32
|
+
p.address.zip.should eq address.zip # without a object schema numbers are converted to strings
|
33
|
+
p.address.phones.should eq address.phones
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_record/attribute_methods/crate_object'
|
2
|
+
|
3
|
+
class Address
|
4
|
+
attr_accessor :street, :city, :phones, :zip
|
5
|
+
|
6
|
+
include CrateObject
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
@street = opts[:street]
|
10
|
+
@city = opts[:city]
|
11
|
+
@phones = opts[:phones]
|
12
|
+
@zip = opts[:zip]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Post do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Migration.class_eval do
|
7
|
+
create_table :posts do |t|
|
8
|
+
t.string :title
|
9
|
+
t.integer :views
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Post.reset_column_information
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
ActiveRecord::Migration.class_eval do
|
17
|
+
drop_table :posts
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:params) { {title: "Crate rocks", views: 10000} }
|
22
|
+
|
23
|
+
context 'initialization' do
|
24
|
+
it 'should initialize a post object with all columns' do
|
25
|
+
post = Post.new(params)
|
26
|
+
post.should be_a(Post)
|
27
|
+
post.title.should eq "Crate rocks"
|
28
|
+
post.views.should eq 10000
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'persistance' do
|
33
|
+
|
34
|
+
it 'should persist the record to the database' do
|
35
|
+
post = Post.create!(params)
|
36
|
+
post.persisted?.should be_true
|
37
|
+
refresh_posts
|
38
|
+
Post.count.should eq 1
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'deletion' do
|
44
|
+
|
45
|
+
before do
|
46
|
+
@post = Post.create!(params)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should destroy the record to the database' do
|
50
|
+
@post.destroy
|
51
|
+
Post.where(id: @post.id).should be_empty
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'existing record manipulation' do
|
56
|
+
before do
|
57
|
+
@post = Post.create!(params)
|
58
|
+
end
|
59
|
+
|
60
|
+
after do
|
61
|
+
#@post.destroy
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'find' do
|
65
|
+
it 'should find the crated record' do
|
66
|
+
post = Post.where(id: @post.id).first
|
67
|
+
post.id.should eq(@post.id)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'update' do
|
72
|
+
it 'should update the record' do
|
73
|
+
@post.update_attributes(title: 'Crate Dope')
|
74
|
+
@post.reload.title.should eq('Crate Dope')
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
3
|
+
require 'activerecord-crate-adapter'
|
4
|
+
require 'logger'
|
5
|
+
#require 'debugger'
|
6
|
+
|
7
|
+
require 'dummy/app/models/address'
|
8
|
+
require 'dummy/app/models/post'
|
9
|
+
require 'dummy/app/models/user'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
|
13
|
+
config.before(:each) do
|
14
|
+
end
|
15
|
+
config.after(:each) do
|
16
|
+
end
|
17
|
+
config.before(:suite) do
|
18
|
+
connect
|
19
|
+
end
|
20
|
+
config.after(:all) do
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect
|
25
|
+
ActiveRecord::Base.logger = Logger.new("log/debug.log")
|
26
|
+
ActiveRecord::Base.logger.level = Logger::DEBUG
|
27
|
+
ActiveRecord::Base.configurations = {
|
28
|
+
'arunit' => {
|
29
|
+
adapter: 'crate',
|
30
|
+
min_messages: 'warning',
|
31
|
+
port: 4209,
|
32
|
+
host: '127.0.0.1'
|
33
|
+
}
|
34
|
+
}
|
35
|
+
ActiveRecord::Base.establish_connection :arunit
|
36
|
+
end
|
37
|
+
|
38
|
+
# Crate is eventually consistent therefore we need
|
39
|
+
# to refresh the table when doing queries, except we
|
40
|
+
# query for the primary key
|
41
|
+
def refresh_posts
|
42
|
+
Post.connection.raw_connection.refresh_table('posts')
|
43
|
+
end
|
data/spec/test_server.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
class TestServer
|
5
|
+
CRATE_PATH = "~/crate"
|
6
|
+
TEST_PORT = 4209
|
7
|
+
NAME = "TestCluster"
|
8
|
+
|
9
|
+
|
10
|
+
def initialize(crate_home = nil, port = nil, host = "127.0.0.1")
|
11
|
+
@crate_home = crate_home || CRATE_PATH
|
12
|
+
@port = port || TEST_PORT
|
13
|
+
@host = host
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
cmd = "sh #{CRATE_PATH}/bin/crate #{start_params}"
|
18
|
+
@pid = spawn(cmd, :out => "/tmp/crate_test_server.out", :err => "/tmp/crate_test_server.err")
|
19
|
+
Process.detach(@pid)
|
20
|
+
puts 'starting'
|
21
|
+
time_slept = 0
|
22
|
+
while true
|
23
|
+
puts "Crate not yet fully available. Waiting since #{time_slept} seconds..." unless alive?
|
24
|
+
sleep(2)
|
25
|
+
time_slept += 2
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop
|
30
|
+
Process.kill("HUP", @pid)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
|
36
|
+
def crate_exec
|
37
|
+
end
|
38
|
+
|
39
|
+
def crate_config
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_params
|
43
|
+
"-Des.index.storage.type=memory " +
|
44
|
+
"-Des.node.name=#{NAME} " +
|
45
|
+
"-Des.cluster.name=Testing#{@port} " +
|
46
|
+
"-Des.http.port=#{@port}-#{@port} " +
|
47
|
+
"-Des.network.host=localhost " +
|
48
|
+
"-Des.discovery.type=zen " +
|
49
|
+
"-Des.discovery.zen.ping.multicast.enabled=false"
|
50
|
+
end
|
51
|
+
|
52
|
+
def alive?
|
53
|
+
req = Net::HTTP::Get.new('/')
|
54
|
+
resp = Net::HTTP.new(@host, @port)
|
55
|
+
begin
|
56
|
+
response = resp.start { |http| http.request(req) }
|
57
|
+
response.code == "200" ? true : false
|
58
|
+
rescue Errno::ECONNREFUSED
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
server = TestServer.new.start *ARGV
|
66
|
+
|
67
|
+
trap("INT") do
|
68
|
+
puts "Script terminated by user."
|
69
|
+
server.stop
|
70
|
+
puts "Server stopped"
|
71
|
+
exit
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-crate-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christoph Klocker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: debugger
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
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: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.14'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activerecord
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 4.0.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.0.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: arel
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.0.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: crate_ruby
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.0.5
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.0.5
|
111
|
+
description: ActiveRecord adapter for Crate, a shared-nothing, fully searchable, document-oriented
|
112
|
+
cluster data store.
|
113
|
+
email:
|
114
|
+
- christoph@vedanova.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- activerecord-crate-adapter.gemspec
|
125
|
+
- history.txt
|
126
|
+
- lib/active_record/attribute_methods/crate_object.rb
|
127
|
+
- lib/active_record/connection_adapters/crate/database_statements.rb
|
128
|
+
- lib/active_record/connection_adapters/crate/quoting.rb
|
129
|
+
- lib/active_record/connection_adapters/crate/schema_statements.rb
|
130
|
+
- lib/active_record/connection_adapters/crate_adapter.rb
|
131
|
+
- lib/activerecord-crate-adapter.rb
|
132
|
+
- lib/activerecord-crate-adapter/version.rb
|
133
|
+
- lib/arel/arel_crate.rb
|
134
|
+
- lib/arel/visitors/crate.rb
|
135
|
+
- log/.keep
|
136
|
+
- spec/activerecord/connection_adapters/crate/crate_adapter_spec.rb
|
137
|
+
- spec/activerecord/connection_adapters/crate/table_definition_spec.rb
|
138
|
+
- spec/data_types/array_spec.rb
|
139
|
+
- spec/data_types/object_spec.rb
|
140
|
+
- spec/dummy/app/models/address.rb
|
141
|
+
- spec/dummy/app/models/post.rb
|
142
|
+
- spec/dummy/app/models/user.rb
|
143
|
+
- spec/models/post_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
- spec/test_server.rb
|
146
|
+
homepage: https://crate.io
|
147
|
+
licenses:
|
148
|
+
- MIT
|
149
|
+
metadata: {}
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
requirements: []
|
165
|
+
rubyforge_project:
|
166
|
+
rubygems_version: 2.2.1
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: ActiveRecord Crate Data Adapter
|
170
|
+
test_files:
|
171
|
+
- spec/activerecord/connection_adapters/crate/crate_adapter_spec.rb
|
172
|
+
- spec/activerecord/connection_adapters/crate/table_definition_spec.rb
|
173
|
+
- spec/data_types/array_spec.rb
|
174
|
+
- spec/data_types/object_spec.rb
|
175
|
+
- spec/dummy/app/models/address.rb
|
176
|
+
- spec/dummy/app/models/post.rb
|
177
|
+
- spec/dummy/app/models/user.rb
|
178
|
+
- spec/models/post_spec.rb
|
179
|
+
- spec/spec_helper.rb
|
180
|
+
- spec/test_server.rb
|
181
|
+
has_rdoc:
|