groovy 0.1.0

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
+ SHA256:
3
+ metadata.gz: 36ab3d8425ae05fd7b069e75b1cb3f168a8a31605e2bd54d3b44285f715dbc92
4
+ data.tar.gz: 7d48ca3d630488d52c0fd60e73ac753d5b1658601f6803566838c563231cce9a
5
+ SHA512:
6
+ metadata.gz: 2dfafb96a0e3636a7e49c9319a40d7e7217d6c21ed48bc0be6e8a4a61aee9a7ce41f9d3499987a03d16e0dc15868988d6a03049a8aec401b70f96b4c405e47b4
7
+ data.tar.gz: 90e22b09f631228277f297c552f42bbdf0bfc9faaccbea13c769b2f68b91cc297c157a1557a101790d0497d2390ba466f4f44f97bf5ac1a3d4f267666b36465e
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *~
2
+ *.gem
3
+ test
4
+ log
5
+ pkg
6
+ example/db
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # specified in groovy.gemspec
4
+ gemspec
data/example/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'rack'
3
+ gem 'groovy', path: '..'
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ groovy (0.1.0)
5
+ rroonga (~> 7.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ archive-zip (0.12.0)
11
+ io-like (~> 0.3.0)
12
+ gqtp (1.0.6)
13
+ groonga-client (0.6.0)
14
+ gqtp (>= 1.0.4)
15
+ groonga-command (>= 1.2.8)
16
+ groonga-command-parser (>= 1.1.0)
17
+ hashie
18
+ groonga-command (1.4.1)
19
+ json
20
+ groonga-command-parser (1.1.4)
21
+ groonga-command (>= 1.4.0)
22
+ json-stream
23
+ hashie (3.6.0)
24
+ io-like (0.3.0)
25
+ json (2.2.0)
26
+ json-stream (0.2.1)
27
+ pkg-config (1.3.7)
28
+ rack (2.0.6)
29
+ rroonga (7.1.1)
30
+ archive-zip
31
+ groonga-client (>= 0.0.3)
32
+ json
33
+ pkg-config
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ groovy!
40
+ rack
41
+
42
+ BUNDLED WITH
43
+ 1.16.1
data/example/config.ru ADDED
@@ -0,0 +1,27 @@
1
+ require 'bundler/setup'
2
+ require 'groovy'
3
+ Groovy.init('./db/test')
4
+
5
+ class Place
6
+ include Groovy::Model
7
+
8
+ # table_options type: :hash
9
+ column :name, String
10
+ column :description, String, searchable: true
11
+ timestamps!
12
+ end
13
+
14
+ Place.delete_all
15
+
16
+ 10.times do |i|
17
+ Place.create!(name: "Place #{i}", description: "A nice place")
18
+ end
19
+
20
+ Place.last.delete
21
+
22
+ app = Proc.new do |env|
23
+ body = Place.all.collect(&:as_json)
24
+ [200, { 'Content-Type' => 'text/plain' }, [body]]
25
+ end
26
+
27
+ run app
data/groovy.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/groovy", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "groovy"
6
+ s.version = Groovy::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Tomás Pollak']
9
+ s.email = ['tomas@forkhq.com']
10
+ s.homepage = "https://github.com/tomas/groovy"
11
+ s.summary = "A wrapper around Groonga/Rroonga"
12
+ s.description = "Allows using Groonga in your models a-la ActiveRecord."
13
+
14
+ # s.required_rubygems_version = ">= 1.3.6"
15
+ # s.rubyforge_project = "groovy"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+ s.add_runtime_dependency "rroonga", "~> 7.1"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
22
+ s.require_path = 'lib'
23
+ end
data/lib/groovy.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'groonga'
2
+ require File.expand_path(File.dirname(__FILE__)) + '/groovy/model'
3
+
4
+ module Groovy
5
+ VERSION = '0.1.0'.freeze
6
+
7
+ def self.init(db_path, opts = {})
8
+ if File.exist?(db_path)
9
+ puts "Opening DB"
10
+ Groonga::Database.open(db_path)
11
+ else
12
+ dir = File.dirname(db_path)
13
+ puts "Creating DB in #{dir}"
14
+ FileUtils.mkdir_p(dir)
15
+ Groonga::Database.create(path: db_path)
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,275 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/query'
2
+ require File.expand_path(File.dirname(__FILE__)) + '/types'
3
+ require File.expand_path(File.dirname(__FILE__)) + '/schema'
4
+
5
+ class Hash
6
+ def symbolize_keys
7
+ self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
8
+ end
9
+ end unless {}.respond_to?(:symbolize_keys)
10
+
11
+ module Groovy
12
+ module Model
13
+
14
+ SEARCH_TABLE_NAME = 'Terms'
15
+
16
+ def self.included(base)
17
+ base.extend(ClassMethods)
18
+ base.include(Forwardable)
19
+ base.table_name = base.name + 's'
20
+
21
+ if base.table.nil?
22
+ # abort "Please set up your database before declaring your models."
23
+ else
24
+ base.extend(PatriciaTrieMethods) if base.table.is_a?(Groonga::PatriciaTrie)
25
+ base.class_eval do
26
+ attribute_names.each do |col|
27
+ add_accessor(col)
28
+ end
29
+ end # if base.table
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ extend Forwardable
35
+ attr_accessor :table_name
36
+
37
+ def validatable!
38
+ include(ActiveModel::Validations)
39
+ end
40
+
41
+ def table
42
+ # raise "Table name not set!" if table_name.nil?
43
+ # schema.table
44
+ Groonga[table_name]
45
+ end
46
+
47
+ def table_options(opts)
48
+ @table_options = opts
49
+ end
50
+
51
+ def schema
52
+ @schema ||= Schema.new(table_name, @table_options)
53
+ end
54
+
55
+ def column(name, type, options = {})
56
+ column_type = Types.map(type)
57
+ schema.column(name, column_type, options)
58
+ add_accessor(name)
59
+ end
60
+
61
+ def timestamps!(opts = {})
62
+ column(:created_at, 'Time')
63
+ column(:updated_at, 'Time')
64
+ end
65
+
66
+ # def columns
67
+ # table.columns # .collect { |x| x.name.split('.').last.to_sym }
68
+ # end
69
+
70
+ def add_accessor(col)
71
+ define_method(col) { self[col] }
72
+ define_method("#{col}=") { |val| self[col] = val }
73
+ end
74
+
75
+ def attribute_names
76
+ table.columns.select { |col| !col.reference_column? }.map { |x| x.name.split('.').last.to_sym }
77
+ end
78
+
79
+ def scope(name, obj)
80
+ define_singleton_method(name) do |*args|
81
+ obj.respond_to?(:call) ? obj.call(*args) : obj
82
+ end
83
+ end
84
+
85
+ def find(id)
86
+ if record = table[id] and record.id
87
+ new(record.attributes, record)
88
+ end
89
+ end
90
+
91
+ def insert(key, attributes = nil)
92
+ if table.support_key?
93
+ table.add(key, attributes)
94
+ else # key is attributes
95
+ set_timestamp(key, :created_at)
96
+ set_timestamp(key, :updated_at)
97
+ table.add(key)
98
+ end
99
+ end
100
+
101
+ def create(key, attributes = nil)
102
+ if record = insert(key, attributes)
103
+ new(record.attributes, record)
104
+ end
105
+ end
106
+
107
+ def create!(key, attributes = nil)
108
+ create(key, attributes) or raise "Invalid!"
109
+ end
110
+
111
+ def delete_all
112
+ schema.rebuild!
113
+ end
114
+
115
+ def search_columns
116
+ @search_columns ||= []
117
+ end
118
+
119
+ def searchable_on(*fields)
120
+ fields.each { |f| add_index_on(f) }
121
+ end
122
+
123
+ def add_index_on(field)
124
+ ensure_search_table
125
+ search_columns.push(field)
126
+ Groonga::Schema.change_table(SEARCH_TABLE_NAME) do |table|
127
+ table.index([table_name, field].join('.'))
128
+ end
129
+ end
130
+
131
+ def ensure_search_table
132
+ return if Groonga[SEARCH_TABLE_NAME]
133
+ Groonga::Schema.create_table(SEARCH_TABLE_NAME, {
134
+ type: :patricia_trie,
135
+ normalizer: :NormalizerAuto,
136
+ default_tokenizer: "TokenBigram"
137
+ })
138
+ end
139
+
140
+ # def column(name)
141
+ # Groonga["#{table_name}.#{name}"] # .search, .similar_search, etc
142
+ # end
143
+
144
+ def similar_search(col, q)
145
+ table.select { |r| r[col].similar_search(q) }
146
+ # table.select("#{col}:#{q}", operator: Groonga::Operation::SIMILAR)
147
+ end
148
+
149
+ def all
150
+ Query.new(self, table)
151
+ end
152
+
153
+ def_instance_delegators :all, :first, :last
154
+
155
+ def find_by(params)
156
+ where(params).first
157
+ end
158
+
159
+ [:search, :where, :not, :sort_by, :limit, :offset].each do |scope_method|
160
+ define_method scope_method do |*args|
161
+ Query.new(self, table).tap do |q|
162
+ q.public_send(scope_method, *args)
163
+ end
164
+ end
165
+ end
166
+
167
+ # this seems to be the same as: `table[id]`
168
+ # def search(key, options = nil)
169
+ # raise "Not supported!" unless table.respond_to?(:search)
170
+ # table.search(key, options)
171
+ # end
172
+
173
+ def_instance_delegators :table, :count, :size
174
+
175
+ def set_timestamp(obj, key_name)
176
+ obj[key_name] = Time.now if attribute_names.include?(key_name.to_sym)
177
+ end
178
+
179
+ end
180
+
181
+ module PatriciaTrieMethods
182
+ extend Forwardable
183
+
184
+ # def_instance_delegators :@table, :scan, :search, :prefix_search, :open_prefix_cursor, :tag_keys
185
+ def_instance_delegators :table, :scan, :search, :prefix_search, :open_prefix_cursor, :tag_keys
186
+
187
+ def each_with_prefix(prefix, options = {}, &block)
188
+ table.open_prefix_cursor(prefix, options) do |cursor|
189
+ cursor.each { |r| yield r }
190
+ end
191
+ end
192
+ end
193
+
194
+ attr_reader :attributes, :record, :changes
195
+
196
+ def initialize(attributes, record = nil)
197
+ @attributes = attributes.symbolize_keys.slice(*self.class.attribute_names)
198
+ @record = record
199
+ @changes = {}
200
+ end
201
+
202
+ def key
203
+ record.respond_to?(:_key) ? record._key : record.id
204
+ end
205
+
206
+ def [](key)
207
+ attributes[key.to_sym]
208
+ end
209
+
210
+ def []=(key, val)
211
+ raise "Invalid attribute: #{key}" unless attributes.has_key?(key.to_sym)
212
+ changes[key.to_sym] = [self[key], val]
213
+ attributes[key.to_sym] = val
214
+ end
215
+
216
+ def increment(values, do_save = true)
217
+ values.each { |key, num| self[key] += num }
218
+ save if do_save
219
+ end
220
+
221
+ def dirty?
222
+ changes.any?
223
+ end
224
+
225
+ def update_attributes(obj)
226
+ obj.each { |k,v| self[k] = v }
227
+ update
228
+ end
229
+
230
+ def save(options = {})
231
+ return false if respond_to?(:invalid) and invalid?
232
+ @record ? update : create
233
+ end
234
+
235
+ def save!(options = {})
236
+ raise "Invalid!" unless save
237
+ end
238
+
239
+ def delete
240
+ record.delete
241
+ self
242
+ end
243
+
244
+ def reload
245
+ raise "Not persisted" if key.nil?
246
+ record = self.class.table[key]
247
+ @attributes = record.attributes.symbolize_keys
248
+ @changes = {}
249
+ self
250
+ end
251
+
252
+ def as_json(options = {})
253
+ attributes
254
+ end
255
+
256
+ private
257
+
258
+ def create
259
+ @record = self.class.insert(attributes)
260
+ self
261
+ end
262
+
263
+ def update
264
+ raise "Not persisted" unless key
265
+ changes.merge.each do |key, values|
266
+ # puts "Updating #{key} from #{values[0]} to #{values[1]}"
267
+ record[key] = values.last
268
+ end
269
+ self.class.set_timestamp(record, :updated_at)
270
+ @changes = {}
271
+ self
272
+ end
273
+
274
+ end
275
+ end
@@ -0,0 +1,182 @@
1
+ module Groovy
2
+
3
+ class Query
4
+ include Enumerable
5
+ AND = '+'.freeze
6
+ NOT = '-'.freeze
7
+
8
+ attr_reader :parameters, :sorting
9
+
10
+ # options:
11
+ # - "operator"
12
+ # - "result"
13
+ # - "name"
14
+ # - "syntax"
15
+ # - "allow_pragma"
16
+ # - "allow_column"
17
+ # - "allow_update"
18
+ # - "allow_leading_not"
19
+ # - "default_column"
20
+
21
+ def initialize(model, table, options = {})
22
+ @model, @table, @options = model, table, options
23
+ @parameters = options.delete(:parameters) || []
24
+ @sorting = { limit: -1, offset: 0 }
25
+ @default_sort_key = table.is_a?(Groonga::Hash) ? '_key' : '_id'
26
+ end
27
+
28
+ def merge_with!(another)
29
+ # parameters.merge!(another.parameters)
30
+ parameters.concat(another.parameters)
31
+ sorting.merge!(another.sorting)
32
+ self
33
+ end
34
+
35
+ def search(obj)
36
+ obj.each do |col, q|
37
+ raise "Not a full-text search column: #{col}" unless model.search_columns.include?(col)
38
+ parameters.push(AND + "(#{col}:@#{q})")
39
+ end
40
+ end
41
+
42
+ # http://groonga.org/docs/reference/grn_expr/query_syntax.html
43
+ # TODO: support match_columns (search value in two or more columns)
44
+ def where(conditions = nil)
45
+ case conditions
46
+ when String # "foo:bar"
47
+ parameters.push(AND + "(#{map_operator(conditions)})")
48
+ when Hash # { foo: 'bar' } or { views: 1..100 }
49
+ conditions.each do |key, val|
50
+ if val.is_a?(Range)
51
+ parameters.push(AND + [key, val.min].join(':>=')) if val.min # lte
52
+ parameters.push(AND + [key, val.max].join(':<=')) if val.max # gte
53
+ elsif val.is_a?(Regexp)
54
+ str = val.source.gsub(/[^a-z]/, '')
55
+ param = val.source[0] == '^' ? ':^' : ':~' # starts with or regexp
56
+ parameters.push(AND + [key, str].join(param))
57
+ else
58
+ parameters.push(AND + [key, val.to_s].join(':'))
59
+ end
60
+ end
61
+ # when Array # ["foo:?", val]
62
+ # parameters.push(conditions.first.sub('?', conditions.last))
63
+ when NilClass
64
+ # doing where.not probably
65
+ else
66
+ raise 'not supported'
67
+ end
68
+ self
69
+ end
70
+
71
+ def not(conditions = {})
72
+ case conditions
73
+ when String # "foo:bar"
74
+ parameters.push(NOT + "(#{map_operator(conditions)})")
75
+ when Hash # { foo: 'bar' }
76
+ conditions.each do |key, val|
77
+ if val.is_a?(Range)
78
+ parameters.push(AND + [key, val.min].join(':<=')) if val.min # gte
79
+ parameters.push(AND + [key, val.max].join(':>=')) if val.max # lte
80
+ else
81
+ parameters.push(AND + [key, val.to_s].join(':!')) # not
82
+ end
83
+ end
84
+ # when Array # ["foo:?", val]
85
+ # parameters.push(conditions.first.sub('?', conditions.last))
86
+ else
87
+ raise 'not supported'
88
+ end
89
+ self
90
+ end
91
+
92
+ def limit(num)
93
+ @limit = num
94
+ self
95
+ end
96
+
97
+ def offset(num)
98
+ @offset = num
99
+ self
100
+ end
101
+
102
+ # sort_by(title: :asc)
103
+ def sort_by(hash)
104
+ sorting[:by] = hash.keys.map do |key|
105
+ { key: key.to_s, order: hash[key] }
106
+ end
107
+ self
108
+ end
109
+
110
+ def all
111
+ @all ||= results.map { |r| model.new(r.attributes['_value']['_key'], r) }
112
+ end
113
+
114
+ def size
115
+ results.size
116
+ end
117
+
118
+ alias_method :count, :size
119
+
120
+ def to_a
121
+ all
122
+ end
123
+
124
+ def [](index)
125
+ all[index]
126
+ end
127
+
128
+ def each(&block)
129
+ all.each { |r| block.call(r) }
130
+ end
131
+
132
+ def last
133
+ all[size-1]
134
+ end
135
+
136
+ private
137
+ attr_reader :model, :table, :options
138
+
139
+ def results
140
+ @results ||= execute
141
+ end
142
+
143
+ def execute
144
+ set = if parameters.any?
145
+ query = parameters.join(" ").gsub(/\s(\w)/, '\ \1')
146
+ puts query if ENV['DEBUG']
147
+ table.select(query, options)
148
+ else
149
+ table.select(options)
150
+ end
151
+
152
+ set.sort(sort_key_and_order, {
153
+ limit: sorting[:limit],
154
+ offset: sorting[:offset]
155
+ })
156
+ end
157
+
158
+ def map_operator(str)
159
+ str.sub(' = ', ':')
160
+ .sub(' != ', ':!')
161
+ .sub(' ~ ', ':~')
162
+ .sub(' < ', ':<')
163
+ .sub(' > ', ':>')
164
+ .sub(' <= ', ':<=')
165
+ .sub(' >= ', ':>=')
166
+ end
167
+
168
+ def sort_key_and_order
169
+ sorting[:by] or [{ key: @default_sort_key, order: :asc }]
170
+ end
171
+
172
+ def method_missing(name, *args)
173
+ if model.respond_to?(name)
174
+ other = model.public_send(name, *args)
175
+ merge_with!(other)
176
+ else
177
+ super
178
+ end
179
+ end
180
+ end
181
+
182
+ end
@@ -0,0 +1,122 @@
1
+ module Groovy
2
+ class Schema
3
+ attr_reader :table, :table_name
4
+
5
+ def initialize(table_name, opts = {})
6
+ @table_name, @opts = table_name, opts
7
+ @columns = {}
8
+ end
9
+
10
+ def table
11
+ @table ||= Groonga[table_name]
12
+ end
13
+
14
+ def rebuild!
15
+ remove_table! if table
16
+ create_table!
17
+ @columns.each do |name, spec|
18
+ check_and_add_column(name, spec[:type], spec[:options])
19
+ end
20
+ puts "done rebuilding"
21
+ end
22
+
23
+ def ensure_created!
24
+ create_table! if table.nil?
25
+ end
26
+
27
+ def column(name, type, options = {})
28
+ ensure_created!
29
+ @columns[name] = { type: type, options: options }
30
+ check_and_add_column(name, type, options)
31
+ end
32
+
33
+ def check_and_add_column(name, type, options)
34
+ add_column(name, type, options) unless has_column?(name, type)
35
+ end
36
+
37
+ def has_column?(name, type)
38
+ table.columns.any? do |col|
39
+ col.name == "#{table_name}.#{name}"
40
+ end
41
+ end
42
+
43
+ def add_column(name, type, options = {})
44
+ # puts "Adding column #{name}"
45
+ Groonga::Schema.change_table(table_name) do |table|
46
+ table.public_send(type, name, options)
47
+ end
48
+ end
49
+
50
+ def create_table!
51
+ # puts "creating table!"
52
+ Groonga::Schema.create_table(table_name, @opts)
53
+ end
54
+
55
+ def remove_table!
56
+ # puts "removing table!"
57
+ Groonga::Schema.remove_table(table_name)
58
+ @table = nil
59
+ end
60
+ end
61
+ end
62
+
63
+ def load_schema!
64
+ Groonga::Schema.create_table("Sources")
65
+ Groonga::Schema.create_table("Topics")
66
+ Groonga::Schema.create_table("Tags", type: :patricia_trie)
67
+ Groonga::Schema.create_table("Links", type: :hash)
68
+ Groonga::Schema.create_table("Tweeters", type: :hash)
69
+ Groonga::Schema.create_table("Mentions")
70
+ Groonga::Schema.create_table("Subscribers")
71
+
72
+ Groonga::Schema.change_table("Sources") do |table|
73
+ table.short_text("name")
74
+ table.short_text("twitter_user_name")
75
+ end
76
+
77
+ Groonga::Schema.change_table("Topics") do |table|
78
+ table.reference("most_mentioned", "Links")
79
+ table.uint32("mentions_count")
80
+ table.uint32("links_count")
81
+ end
82
+
83
+ Groonga::Schema.change_table("Links") do |table|
84
+ table.reference("source", "Sources")
85
+ table.reference("topic", "Topics")
86
+ table.reference("tags", "Tags", type: :vector)
87
+ # table.short_text("url")
88
+ table.short_text("title")
89
+ table.short_text("image")
90
+ table.short_text("description") # less than 4K bytes
91
+ table.uint32("mentions_count")
92
+ table.uint32("hits_count")
93
+ table.boolean("posted")
94
+ table.boolean("removed")
95
+ table.short_text("snapshot")
96
+ table.uint32("posted_tweet_id")
97
+ end
98
+
99
+ Groonga::Schema.change_table("Tweeters") do |table|
100
+ table.short_text("name")
101
+ table.short_text("twitter_name")
102
+ table.uint32("mentions_count")
103
+ end
104
+
105
+ Groonga::Schema.change_table("Mentions") do |table|
106
+ table.reference("source", "Sources")
107
+ table.reference("link", "Links")
108
+ table.reference("tweeter", "Tweeters")
109
+ table.uint32("tweet_id")
110
+ table.short_text("content")
111
+ table.time("created_at")
112
+ end
113
+
114
+ Groonga::Schema.change_table("Subscribers") do |table|
115
+ table.short_text("name")
116
+ table.short_text("email")
117
+ table.short_text("timezone")
118
+ table.short_text("token")
119
+ table.uint32("received_count")
120
+ table.time("last_received_at")
121
+ end
122
+ end
@@ -0,0 +1,44 @@
1
+ module Groovy
2
+
3
+ module Types
4
+
5
+ # BIGRAM
6
+ # BOOL
7
+ # BOOLEAN
8
+ # DELIMIT
9
+ # FLOAT
10
+ # INT16
11
+ # INT32
12
+ # INT64
13
+ # INT8
14
+ # LONG_TEXT
15
+ # MECAB
16
+ # OBJECT
17
+ # SHORT_TEXT
18
+ # TEXT
19
+ # TIME
20
+ # TRIGRAM
21
+ # UINT16
22
+ # UINT32
23
+ # UINT64
24
+ # UINT8
25
+ # UNIGRAM
26
+
27
+ MAPPINGS = {
28
+ 'String' => 'short_text',
29
+ 'Text' => 'text',
30
+ 'Float' => 'float',
31
+ 'Bool' => 'boolean',
32
+ 'Boolean' => 'boolean',
33
+ 'Integer' => 'int32',
34
+ 'BigDecimal' => 'int64',
35
+ 'Time' => 'time'
36
+ }
37
+
38
+ def self.map(type)
39
+ MAPPINGS[type.to_s] or raise "Invalid type: #{type}"
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,3 @@
1
+ require './lib/groovy'
2
+ require 'rspec/mocks'
3
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: groovy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tomás Pollak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-02 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.0.0
20
+ type: :development
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: rroonga
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7.1'
41
+ description: Allows using Groonga in your models a-la ActiveRecord.
42
+ email:
43
+ - tomas@forkhq.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - example/Gemfile
51
+ - example/Gemfile.lock
52
+ - example/config.ru
53
+ - groovy.gemspec
54
+ - lib/groovy.rb
55
+ - lib/groovy/model.rb
56
+ - lib/groovy/query.rb
57
+ - lib/groovy/schema.rb
58
+ - lib/groovy/types.rb
59
+ - spec/groovy_spec.rb
60
+ homepage: https://github.com/tomas/groovy
61
+ licenses: []
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.7.3
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: A wrapper around Groonga/Rroonga
83
+ test_files: []