findable 0.2.1 → 0.2.2
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 +4 -4
- data/lib/findable/associations/utils.rb +1 -1
- data/lib/findable/base.rb +25 -13
- data/lib/findable/collection.rb +5 -0
- data/lib/findable/query.rb +66 -5
- data/lib/findable/schema.rb +14 -1
- data/lib/findable/schema/conversion.rb +5 -5
- data/lib/findable/seed.rb +60 -26
- data/lib/findable/version.rb +1 -1
- data/lib/generators/findable/templates/seeds.rb +1 -1
- data/spec/findable/base_spec.rb +4 -4
- data/spec/findable/query_spec.rb +1 -1
- data/spec/findable/schema/conversion_spec.rb +11 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79d36740a44c4a4a702069dab24e53b533e8db00
|
4
|
+
data.tar.gz: 2908163cf00a3546837f8a0fa5b3e20bad62b044
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2761e6fbd194a70df6ecab268d507a4b689520b537c3210e83f9d1a59d1528a6df5085fcd23d9c5b129f60a3d8037135fa310e2a7c861f9a70be1a0a3fccd709
|
7
|
+
data.tar.gz: fdef311c4c618ee709ca488157753480ba2b2fc2bf61d6e1060d904d8aa9c9a1253c18d14d3e20fa3705a14581af019bd19e14950c2d024e73ec554b97d0c90b
|
data/lib/findable/base.rb
CHANGED
@@ -5,7 +5,6 @@ require "findable/inspection"
|
|
5
5
|
module Findable
|
6
6
|
class Base
|
7
7
|
include ActiveModel::Model
|
8
|
-
include ActiveModel::AttributeMethods
|
9
8
|
include Associations
|
10
9
|
include Schema
|
11
10
|
include Inspection
|
@@ -39,8 +38,14 @@ module Findable
|
|
39
38
|
def find_by(conditions)
|
40
39
|
if conditions.is_a?(Hash)
|
41
40
|
conditions.symbolize_keys!
|
42
|
-
if
|
43
|
-
|
41
|
+
if index = conditions.keys.detect {|key| key.in?(indexes) }
|
42
|
+
value = conditions.delete(index)
|
43
|
+
if index == :id
|
44
|
+
records = find_by_ids(value)
|
45
|
+
else
|
46
|
+
records = find_by_index(index, value)
|
47
|
+
end
|
48
|
+
|
44
49
|
case
|
45
50
|
when records.empty? then nil
|
46
51
|
when conditions.empty? then records.first
|
@@ -63,8 +68,14 @@ module Findable
|
|
63
68
|
|
64
69
|
def where(conditions)
|
65
70
|
conditions.symbolize_keys!
|
66
|
-
if
|
67
|
-
|
71
|
+
if index = conditions.keys.detect {|key| key.in?(indexes) }
|
72
|
+
value = conditions.delete(index)
|
73
|
+
if index == :id
|
74
|
+
records = find_by_ids(value)
|
75
|
+
else
|
76
|
+
records = find_by_index(index, value)
|
77
|
+
end
|
78
|
+
|
68
79
|
if conditions.empty?
|
69
80
|
collection!(records)
|
70
81
|
else
|
@@ -84,10 +95,17 @@ module Findable
|
|
84
95
|
end
|
85
96
|
alias_method :create!, :create
|
86
97
|
|
98
|
+
## Extension
|
99
|
+
|
100
|
+
def ordered_find(*_ids)
|
101
|
+
_ids.flatten!
|
102
|
+
find(_ids).ordered_find(_ids)
|
103
|
+
end
|
104
|
+
|
87
105
|
## Query APIs
|
88
106
|
|
89
|
-
delegate :find_by_ids, :insert, to: :query
|
90
|
-
delegate :count, :ids, :delete_all, to: :query
|
107
|
+
delegate :find_by_ids, :find_by_index, :insert, to: :query
|
108
|
+
delegate :count, :ids, :delete, :delete_all, to: :query
|
91
109
|
alias_method :destroy_all, :delete_all
|
92
110
|
|
93
111
|
def exists?(obj)
|
@@ -98,12 +116,6 @@ module Findable
|
|
98
116
|
end
|
99
117
|
end
|
100
118
|
|
101
|
-
def delete(obj)
|
102
|
-
if _id = id_from(obj)
|
103
|
-
query.delete(_id)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
119
|
def query
|
108
120
|
@_query ||= Query.new(self)
|
109
121
|
end
|
data/lib/findable/collection.rb
CHANGED
data/lib/findable/query.rb
CHANGED
@@ -31,6 +31,12 @@ module Findable
|
|
31
31
|
@serializer.deserialize(redis.hmget(data_key, *Array(ids)), model)
|
32
32
|
end
|
33
33
|
|
34
|
+
def find_by_index(index, value)
|
35
|
+
if ids = ids_from_index([index, value].join(":"))
|
36
|
+
find_by_ids(ids)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
34
40
|
def exists?(id)
|
35
41
|
redis.hexists(data_key, id)
|
36
42
|
end
|
@@ -43,28 +49,51 @@ module Findable
|
|
43
49
|
object.id,
|
44
50
|
@serializer.serialize(object.attributes)
|
45
51
|
)
|
52
|
+
update_index(object)
|
46
53
|
end
|
47
54
|
object
|
48
55
|
end
|
49
56
|
|
50
57
|
def import(hashes)
|
51
58
|
lock do
|
52
|
-
|
59
|
+
indexes = Hash.new {|h, k| h[k] = [] }
|
60
|
+
values = hashes.each_with_object([]) do |hash, obj|
|
61
|
+
hash = hash.with_indifferent_access
|
53
62
|
hash["id"] = auto_incremented_id(hash["id"])
|
54
63
|
obj << hash["id"]
|
55
64
|
obj << @serializer.serialize(hash)
|
65
|
+
|
66
|
+
if model.index_defined?
|
67
|
+
model.indexes.each_with_object([]) do |name, obj|
|
68
|
+
next if name == :id
|
69
|
+
indexes[[name, hash[name]].join(":")] << hash["id"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
redis.hmset(data_key, *values)
|
74
|
+
if indexes.present?
|
75
|
+
attrs = indexes.map {|k, v| [k, @serializer.serialize(v)] }.flatten
|
76
|
+
redis.hmset(index_key, *attrs)
|
56
77
|
end
|
57
|
-
redis.hmset(data_key, *auto_incremented)
|
58
78
|
end
|
59
79
|
end
|
60
80
|
|
61
|
-
def delete(
|
62
|
-
|
81
|
+
def delete(object)
|
82
|
+
if model.index_defined?
|
83
|
+
model.indexes.each do |name|
|
84
|
+
next if name == :id
|
85
|
+
if value = object.public_send("#{name}_was") || object.public_send(name)
|
86
|
+
redis.hdel(index_key, value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
redis.hdel(data_key, object.id)
|
63
92
|
end
|
64
93
|
|
65
94
|
def delete_all
|
66
95
|
redis.multi do
|
67
|
-
[data_key, info_key].each {|key| redis.del(key) }
|
96
|
+
[data_key, info_key, index_key].each {|key| redis.del(key) }
|
68
97
|
end
|
69
98
|
end
|
70
99
|
|
@@ -73,6 +102,32 @@ module Findable
|
|
73
102
|
Lock.new(lock_key, thread_key).lock { yield }
|
74
103
|
end
|
75
104
|
|
105
|
+
def update_index(object)
|
106
|
+
if model.index_defined?
|
107
|
+
indexes = model.indexes.each_with_object([]) {|name, obj|
|
108
|
+
next if name == :id || object.public_send("#{name}_changed?")
|
109
|
+
|
110
|
+
if old_value = object.public_send("#{name}_was")
|
111
|
+
old_index_key = [name, old_value].join(":")
|
112
|
+
|
113
|
+
if (old_ids = ids_from_index(old_index_key)).present?
|
114
|
+
new_ids = old_ids.reject {|id| id == object.id }
|
115
|
+
if new_ids.empty?
|
116
|
+
redis.hdel(index_key, old_index_key)
|
117
|
+
else
|
118
|
+
obj << old_index_key
|
119
|
+
obj << @serializer.serialize(new_ids)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
obj << [name, object.public_send(name)].join(":")
|
125
|
+
obj << object.id
|
126
|
+
}
|
127
|
+
redis.hmset(index_key, *indexes)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
76
131
|
private
|
77
132
|
def auto_incremented_id(id)
|
78
133
|
if id.present?
|
@@ -86,5 +141,11 @@ module Findable
|
|
86
141
|
redis.hincrby(info_key, AUTO_INCREMENT_KEY, 1)
|
87
142
|
end
|
88
143
|
end
|
144
|
+
|
145
|
+
def ids_from_index(index_name)
|
146
|
+
if ids = redis.hget(index_key, index_name)
|
147
|
+
@serializer.deserialize(ids)
|
148
|
+
end
|
149
|
+
end
|
89
150
|
end
|
90
151
|
end
|
data/lib/findable/schema.rb
CHANGED
@@ -5,6 +5,9 @@ module Findable
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
+
include ActiveModel::AttributeMethods
|
9
|
+
include ActiveModel::Dirty
|
10
|
+
|
8
11
|
attribute_method_suffix "="
|
9
12
|
attribute_method_suffix "?"
|
10
13
|
end
|
@@ -14,19 +17,29 @@ module Findable
|
|
14
17
|
@_column_names ||= [:id]
|
15
18
|
end
|
16
19
|
|
20
|
+
def indexes
|
21
|
+
@_indexes ||= [:id]
|
22
|
+
end
|
23
|
+
|
24
|
+
def index_defined?
|
25
|
+
indexes.size > 1
|
26
|
+
end
|
27
|
+
|
17
28
|
def define_field(*args)
|
18
29
|
options = args.extract_options!
|
19
30
|
name = args.first
|
20
31
|
if !public_method_defined?(name) || options.present?
|
21
32
|
define_attribute_methods name
|
22
|
-
conversion = Findable::Schema::Conversion.
|
33
|
+
conversion = Findable::Schema::Conversion.to(options[:type])
|
23
34
|
define_method(name) { conversion.call(attributes[name.to_sym]) }
|
35
|
+
indexes << name.to_sym if options[:index]
|
24
36
|
column_names << name.to_sym
|
25
37
|
end
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
41
|
def attribute=(attr, value)
|
42
|
+
public_send("#{attr}_will_change!")
|
30
43
|
attributes[attr] = value
|
31
44
|
end
|
32
45
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Findable
|
2
2
|
module Schema
|
3
|
-
|
3
|
+
module Conversion
|
4
4
|
class << self
|
5
5
|
FALSE_VALUE = ["false", "0"]
|
6
6
|
|
7
|
-
def
|
7
|
+
def to(type)
|
8
8
|
return types[:default] if type.nil?
|
9
|
-
types[type] || add_type!(type)
|
9
|
+
types[type.to_sym] || add_type!(type)
|
10
10
|
end
|
11
11
|
|
12
12
|
def types
|
@@ -16,8 +16,8 @@ module Findable
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def add_type!(type)
|
19
|
-
return type if type.
|
20
|
-
types[type.to_sym] = method(type)
|
19
|
+
return type if type.is_a?(Proc)
|
20
|
+
types[type.to_sym] = method(type).to_proc
|
21
21
|
end
|
22
22
|
|
23
23
|
def clear_types
|
data/lib/findable/seed.rb
CHANGED
@@ -1,14 +1,50 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "csv"
|
3
|
+
|
1
4
|
module Findable
|
2
|
-
class
|
5
|
+
class UnknownSeedDir < FindableError
|
6
|
+
def initialize(path)
|
7
|
+
if path
|
8
|
+
super("Couldn't find #{path}")
|
9
|
+
else
|
10
|
+
super("There is no configuration")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Seed < Struct.new(:seed_files, :model_name)
|
3
16
|
class << self
|
4
17
|
def target_files(seed_dir: nil, seed_files: nil)
|
5
|
-
target_dir = pathname(seed_dir
|
6
|
-
raise
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
18
|
+
target_dir = pathname(seed_dir || Findable.config.seed_dir)
|
19
|
+
raise UnknownSeedDir.new(target_dir) unless target_dir.try(:exist?)
|
20
|
+
|
21
|
+
seed_files = seed_files.map!(&:to_s) if seed_files
|
22
|
+
_model_name = method(:model_name).to_proc.curry.call(target_dir)
|
23
|
+
_selected = Proc.new do |seed|
|
24
|
+
seed_files.present? ? seed.table_name.in?(seed_files) : true
|
25
|
+
end
|
26
|
+
|
27
|
+
Pathname.glob(target_dir.join("**", "*"))
|
28
|
+
.select(&:file?)
|
29
|
+
.group_by(&_model_name)
|
30
|
+
.map {|name, files| new(files, name) }
|
31
|
+
.select(&_selected)
|
32
|
+
end
|
33
|
+
|
34
|
+
def model_name(seed_dir, seed_file)
|
35
|
+
if seed_dir != seed_file.dirname && seed_file.basename.to_s.match(/^data/)
|
36
|
+
from_seed_dir(seed_dir, seed_file.dirname).to_s.classify
|
37
|
+
else
|
38
|
+
from_seed_dir(seed_dir, without_ext(seed_file)).to_s.classify
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def from_seed_dir(seed_dir, seed_file)
|
43
|
+
pathname(seed_file).relative_path_from(seed_dir)
|
44
|
+
end
|
45
|
+
|
46
|
+
def without_ext(seed_file)
|
47
|
+
pathname(seed_file).dirname.join(pathname(seed_file).basename(".*"))
|
12
48
|
end
|
13
49
|
|
14
50
|
def pathname(path)
|
@@ -20,37 +56,35 @@ module Findable
|
|
20
56
|
end
|
21
57
|
end
|
22
58
|
|
23
|
-
def
|
24
|
-
@
|
25
|
-
@_seed_dir = seed_dir
|
26
|
-
end
|
27
|
-
|
28
|
-
def basename
|
29
|
-
@_basename ||= @_full_path.basename(".*").to_s
|
59
|
+
def model
|
60
|
+
@_model_class ||= model_name.constantize
|
30
61
|
end
|
31
62
|
|
32
|
-
def
|
33
|
-
|
63
|
+
def load_files
|
64
|
+
seed_files.sort_by(&:to_s).inject([]) do |data, file|
|
65
|
+
data | case file.extname
|
66
|
+
when ".yml" then load_yaml(file)
|
67
|
+
when ".csv" then load_csv(file)
|
68
|
+
else
|
69
|
+
raise UnknownFormat
|
70
|
+
end
|
71
|
+
end
|
34
72
|
end
|
35
73
|
|
36
74
|
def bootstrap!
|
37
75
|
model.query.lock do
|
38
76
|
model.delete_all
|
39
|
-
model.query.import
|
77
|
+
model.query.import load_files
|
40
78
|
end
|
41
79
|
end
|
42
80
|
|
43
81
|
private
|
44
|
-
def
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
def without_ext(path)
|
49
|
-
pathname(path).dirname.join(pathname(path).basename(".*"))
|
82
|
+
def load_yaml(seed_file)
|
83
|
+
YAML.load_file(seed_file).values
|
50
84
|
end
|
51
85
|
|
52
|
-
def
|
53
|
-
|
86
|
+
def load_csv(seed_file)
|
87
|
+
CSV.table(seed_file).map(&:to_h)
|
54
88
|
end
|
55
89
|
end
|
56
90
|
end
|
data/lib/findable/version.rb
CHANGED
data/spec/findable/base_spec.rb
CHANGED
@@ -125,12 +125,12 @@ describe Findable::Base do
|
|
125
125
|
|
126
126
|
# Private instance methods
|
127
127
|
describe "#attribute=" do
|
128
|
-
before { instance.send(:attribute=, :
|
129
|
-
it { expect(instance.attributes[:
|
128
|
+
before { instance.send(:attribute=, :name, "value") }
|
129
|
+
it { expect(instance.attributes[:name]).to eq("value") }
|
130
130
|
end
|
131
131
|
|
132
132
|
describe "#attribute?" do
|
133
|
-
before { instance.send(:attribute=, :
|
134
|
-
it { expect(instance.send(:attribute?, :
|
133
|
+
before { instance.send(:attribute=, :name, "value") }
|
134
|
+
it { expect(instance.send(:attribute?, :name)).to be_truthy }
|
135
135
|
end
|
136
136
|
end
|
data/spec/findable/query_spec.rb
CHANGED
@@ -4,17 +4,17 @@ describe Findable::Schema::Conversion do
|
|
4
4
|
after(:each) { conversion.clear_types }
|
5
5
|
let(:conversion) { Findable::Schema::Conversion }
|
6
6
|
|
7
|
-
describe ".
|
8
|
-
it { expect(conversion.
|
9
|
-
it { expect(conversion.
|
10
|
-
it { expect(conversion.
|
11
|
-
it { expect(conversion.
|
12
|
-
it { expect(conversion.
|
13
|
-
it { expect(conversion.
|
14
|
-
it { expect(conversion.
|
15
|
-
it { expect(conversion.
|
16
|
-
it { expect(conversion.
|
17
|
-
it { expect(conversion.
|
7
|
+
describe ".to" do
|
8
|
+
it { expect(conversion.to(nil)).to eq(conversion.types[:default]) }
|
9
|
+
it { expect(conversion.to(:integer)).to eq(conversion.types[:integer]) }
|
10
|
+
it { expect(conversion.to(:float)).to eq(conversion.types[:float]) }
|
11
|
+
it { expect(conversion.to(:decimal)).to eq(conversion.types[:decimal]) }
|
12
|
+
it { expect(conversion.to(:string)).to eq(conversion.types[:string]) }
|
13
|
+
it { expect(conversion.to(:boolean)).to eq(conversion.types[:boolean]) }
|
14
|
+
it { expect(conversion.to(:date)).to eq(conversion.types[:date]) }
|
15
|
+
it { expect(conversion.to(:datetime)).to eq(conversion.types[:datetime]) }
|
16
|
+
it { expect(conversion.to(:symbol)).to eq(conversion.types[:symbol]) }
|
17
|
+
it { expect(conversion.to(:inquiry)).to eq(conversion.types[:inquiry]) }
|
18
18
|
end
|
19
19
|
|
20
20
|
describe ".types" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: findable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- i2bskn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|