findable 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 87900a069a5960243f4231d883d417f0b39c319c
4
- data.tar.gz: 44fe61ca87bf212c297cacec0dd8c429dc371d65
3
+ metadata.gz: 4305f6cde690c51441bbdd0da26eae24b10d7b04
4
+ data.tar.gz: 40f6aedbad7fdc92cb81b4a423712b118111eb02
5
5
  SHA512:
6
- metadata.gz: c65b156840c789ea9338d6e574a67befcc28c6f8c3dfcdcff59aef446d3a37bb754b1018d2904b988e26783af48d6a2fa2676fdd570d04f7b385dcda3d079210
7
- data.tar.gz: 9c851c3620d17317a35aeda778b80b23997ef0778e44e1de5750d5f403bf324e9eb12920b81726c3724ce54e4e5814241a24e423f3fa970fa424b885561841b0
6
+ metadata.gz: 1535776a557a195a0a7757446136ab67a834a66c08dd28a952b535037110eb0eff880e322b78c8ecaeba1181b47309e1a21c11c3aa9191874811b132554799de
7
+ data.tar.gz: 918c3a3636e3c6dc952e0aa3544acc04843c61dc8ac6412975f04102c350fc5d3d001fb1fa3349c97fc056bec11906c87dd30f61f83569a1eb356aa45e3dde67
data/.coveralls.yml CHANGED
@@ -1,2 +1 @@
1
1
  repo_token: fbWiPxpNWcR8fOyueZ7IWw1pMbl20ZvRN
2
-
data/.gitignore CHANGED
@@ -13,4 +13,3 @@
13
13
  *.a
14
14
  mkmf.log
15
15
  .DS_Store
16
-
data/.travis.yml CHANGED
@@ -7,4 +7,3 @@ gemfile:
7
7
  script: bundle exec rake spec
8
8
  services:
9
9
  - redis-server
10
-
data/README.md CHANGED
@@ -7,6 +7,10 @@
7
7
 
8
8
  Redis wrapper with API like ActiveRecord.
9
9
 
10
+ ## Requirements
11
+
12
+ - Redis
13
+
10
14
  ## Installation
11
15
 
12
16
  Add this line to your application's Gemfile:
data/findable.gemspec CHANGED
@@ -20,13 +20,14 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "activesupport"
22
22
  spec.add_dependency "activemodel"
23
- spec.add_dependency "redis-objects"
23
+ spec.add_dependency "redis"
24
24
  spec.add_dependency "oj"
25
25
 
26
26
  spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "rake"
28
28
  spec.add_development_dependency "rspec"
29
29
  spec.add_development_dependency "activerecord"
30
+ spec.add_development_dependency "sqlite3"
30
31
  spec.add_development_dependency "pry"
31
32
  spec.add_development_dependency "coveralls"
32
33
  spec.add_development_dependency "simplecov"
data/lib/findable.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "active_support"
2
2
  require "active_support/core_ext"
3
3
  require "active_model"
4
- require "redis/objects"
4
+ require "redis"
5
5
  require "oj"
6
6
 
7
7
  require "findable/version"
@@ -15,14 +15,7 @@ module Findable
15
15
  model.where(foreign_key => public_send(self.class.primary_key))
16
16
  end
17
17
 
18
- reflection = ActiveRecord::Reflection.create(
19
- :has_many,
20
- name.to_sym,
21
- nil,
22
- options,
23
- self,
24
- )
25
- ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
18
+ Utils.add_reflection(:has_many, name.to_sym, options, self)
26
19
  else
27
20
  super
28
21
  end
@@ -39,14 +32,7 @@ module Findable
39
32
  model.find_by(foreign_key => public_send(self.class.primary_key))
40
33
  end
41
34
 
42
- reflection = ActiveRecord::Reflection.create(
43
- :has_one,
44
- name.to_sym,
45
- nil,
46
- options,
47
- self,
48
- )
49
- ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
35
+ Utils.add_reflection(:has_one, name.to_sym, options, self)
50
36
  else
51
37
  super
52
38
  end
@@ -68,14 +54,7 @@ module Findable
68
54
  public_send("#{foreign_key}=", value)
69
55
  end
70
56
 
71
- reflection = ActiveRecord::Reflection.create(
72
- :belongs_to,
73
- name.to_sym,
74
- nil,
75
- options,
76
- self,
77
- )
78
- ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
57
+ Utils.add_reflection(:belongs_to, name.to_sym, options, self)
79
58
  else
80
59
  super
81
60
  end
@@ -19,6 +19,17 @@ module Findable
19
19
  options = copied.extract_options!
20
20
  [copied.first, options]
21
21
  end
22
+
23
+ def self.add_reflection(macro, name, options, ar)
24
+ reflection = ActiveRecord::Reflection.create(
25
+ macro.to_sym,
26
+ name.to_sym,
27
+ nil,
28
+ options,
29
+ ar
30
+ )
31
+ ActiveRecord::Reflection.add_reflection(ar, name.to_sym, reflection)
32
+ end
22
33
  end
23
34
  end
24
35
  end
data/lib/findable/base.rb CHANGED
@@ -13,9 +13,8 @@ module Findable
13
13
  class << self
14
14
  ## field definitions
15
15
 
16
- def define_field(attr, options = {})
16
+ def define_field(attr)
17
17
  unless public_method_defined?(attr)
18
- options.symbolize_keys!
19
18
  define_attribute_methods attr
20
19
  define_method(attr) { attributes[attr.to_sym] }
21
20
  column_names << attr.to_sym
@@ -8,10 +8,6 @@ module Findable
8
8
  end
9
9
  end
10
10
 
11
- class ModelNotFound < FindableError
12
- def initialize(model_name)
13
- super("#{model_name} not found.")
14
- end
15
- end
11
+ class LockTimeout < FindableError; end
16
12
  end
17
13
 
@@ -1,5 +1,6 @@
1
1
  require "findable/query/connection"
2
2
  require "findable/query/namespace"
3
+ require "findable/query/lock"
3
4
 
4
5
  module Findable
5
6
  class Query
@@ -27,7 +28,7 @@ module Findable
27
28
  end
28
29
 
29
30
  def insert(hash)
30
- transaction do
31
+ lock do
31
32
  hash[:id] = auto_incremented_id(hash[:id])
32
33
  redis.hset(data_key, hash[:id], Oj.dump(hash))
33
34
  end
@@ -35,7 +36,7 @@ module Findable
35
36
  end
36
37
 
37
38
  def import(hashes)
38
- transaction do
39
+ lock do
39
40
  auto_incremented = hashes.each_with_object([]) do |hash, obj|
40
41
  hash["id"] = auto_incremented_id(hash["id"])
41
42
  obj << hash["id"]
@@ -55,22 +56,9 @@ module Findable
55
56
  end
56
57
  end
57
58
 
58
- def transaction
59
+ def lock
59
60
  raise ArgumentError, "Require block" unless block_given?
60
- if Thread.current[thread_key]
61
- yield
62
- else
63
- begin
64
- Thread.current[thread_key] = true
65
- Redis::Lock.new(lock_key).lock do
66
- yield
67
- end
68
- rescue Redis::Lock::LockTimeout
69
- raise
70
- ensure
71
- Thread.current[thread_key] = nil
72
- end
73
- end
61
+ Lock.new(lock_key, thread_key).lock { yield }
74
62
  end
75
63
 
76
64
  private
@@ -11,7 +11,7 @@ module Findable
11
11
  # Generate connection with redis options or default connection.
12
12
  # @return [Redis] Redis connection
13
13
  def generate_redis_connection!
14
- redis_options ? Redis.new(*redis_options) : Redis.current
14
+ redis_options ? Redis.new(**redis_options) : Redis.current
15
15
  end
16
16
 
17
17
  # Returns redis options from configuration.
@@ -0,0 +1,59 @@
1
+ require "findable/query/connection"
2
+
3
+ module Findable
4
+ class Query
5
+ class Lock
6
+ include Findable::Query::Connection
7
+
8
+ def initialize(lock_key, thread_key, options = {})
9
+ @lock_key = lock_key
10
+ @thread_key = thread_key
11
+ @options = options.symbolize_keys!
12
+ end
13
+
14
+ def lock
15
+ if Thread.current[@thread_key]
16
+ yield
17
+ else
18
+ Thread.current[@thread_key] = true
19
+ try_lock!(Time.current)
20
+ begin
21
+ yield
22
+ ensure
23
+ Thread.current[@thread_key] = nil
24
+ unlock!
25
+ end
26
+ end
27
+ end
28
+
29
+ def unlock!
30
+ redis.del(@lock_key)
31
+ end
32
+
33
+ private
34
+ def try_lock!(start)
35
+ loop do
36
+ break if redis.setnx(@lock_key, expiration)
37
+
38
+ current = redis.get(@lock_key).to_f
39
+ if current < Time.current.to_f
40
+ old = redis.getset(@lock_key, expiration).to_f
41
+ break if old < Time.current.to_f
42
+ end
43
+
44
+ Kernel.sleep(0.1)
45
+ raise Findable::LockTimeout if (Time.current.to_f - start) > timeout
46
+ end
47
+ end
48
+
49
+ def expiration
50
+ (Time.current + timeout).to_f
51
+ end
52
+
53
+ def timeout
54
+ (@options[:timeout] || 5).to_f
55
+ end
56
+ end
57
+ end
58
+ end
59
+
@@ -1,4 +1,4 @@
1
1
  module Findable
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
4
4
 
@@ -1,71 +1,71 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe ActiveModel::Lint::Tests do
4
- include_context "FindableModels"
4
+ include_context "TemporaryModel"
5
5
 
6
6
  context "#test_to_key" do
7
7
  it "The model should respond to to_key" do
8
- expect(findable_instance).to respond_to(:to_key)
8
+ expect(instance).to respond_to(:to_key)
9
9
  end
10
10
 
11
11
  it "to_key should return nil when `persisted?` returns false" do
12
- allow(findable_instance).to receive(:persisted?).and_return(false)
13
- expect(findable_instance.to_key).to be_nil
12
+ allow(instance).to receive(:persisted?).and_return(false)
13
+ expect(instance.to_key).to be_nil
14
14
  end
15
15
  end
16
16
 
17
17
  context "#test_to_param" do
18
18
  it "The model should respond to to_param" do
19
- expect(findable_instance).to respond_to(:to_param)
19
+ expect(instance).to respond_to(:to_param)
20
20
  end
21
21
 
22
22
  it "to_param should return nil when `persisted?` returns false" do
23
- allow(findable_instance).to receive(:to_key).and_return([1])
24
- allow(findable_instance).to receive(:persisted?).and_return(false)
25
- expect(findable_instance.to_param).to be_nil
23
+ allow(instance).to receive(:to_key).and_return([1])
24
+ allow(instance).to receive(:persisted?).and_return(false)
25
+ expect(instance.to_param).to be_nil
26
26
  end
27
27
  end
28
28
 
29
29
  context "#test_to_partial_path" do
30
30
  it "The model should respond to to_partial_path" do
31
- expect(findable_instance).to respond_to(:to_partial_path)
31
+ expect(instance).to respond_to(:to_partial_path)
32
32
  end
33
33
 
34
- it { expect(findable_instance.to_partial_path).to be_kind_of(String) }
34
+ it { expect(instance.to_partial_path).to be_kind_of(String) }
35
35
  end
36
36
 
37
37
  context "#test_persisted?" do
38
38
  it "The model should respond to persisted?" do
39
- expect(findable_instance).to respond_to(:persisted?)
39
+ expect(instance).to respond_to(:persisted?)
40
40
  end
41
41
 
42
42
  it "persisted?" do
43
- bool = findable_instance.persisted?
43
+ bool = instance.persisted?
44
44
  expect(bool == true || bool == false).to be_truthy
45
45
  end
46
46
  end
47
47
 
48
48
  context "#test_model_naming" do
49
49
  it "The model class should respond to model_name" do
50
- expect(findable_instance).to respond_to(:model_name)
50
+ expect(instance).to respond_to(:model_name)
51
51
  end
52
52
 
53
- it { expect(findable_model.model_name).to respond_to(:to_str) }
54
- it { expect(findable_model.model_name.human).to respond_to(:to_str) }
55
- it { expect(findable_model.model_name.singular).to respond_to(:to_str) }
56
- it { expect(findable_model.model_name.plural).to respond_to(:to_str) }
53
+ it { expect(model.model_name).to respond_to(:to_str) }
54
+ it { expect(model.model_name.human).to respond_to(:to_str) }
55
+ it { expect(model.model_name.singular).to respond_to(:to_str) }
56
+ it { expect(model.model_name.plural).to respond_to(:to_str) }
57
57
 
58
- it { expect(findable_instance).to respond_to(:model_name) }
59
- it { expect(findable_instance.model_name).to eq(findable_model.model_name) }
58
+ it { expect(instance).to respond_to(:model_name) }
59
+ it { expect(instance.model_name).to eq(model.model_name) }
60
60
  end
61
61
 
62
62
  context "#test_errors_aref" do
63
63
  it "The model should respond to errors" do
64
- expect(findable_instance).to respond_to(:errors)
64
+ expect(instance).to respond_to(:errors)
65
65
  end
66
66
 
67
67
  it "errors#[] should return an Array" do
68
- expect(findable_instance.errors[:hello]).to be_kind_of(Array)
68
+ expect(instance.errors[:hello]).to be_kind_of(Array)
69
69
  end
70
70
  end
71
71
  end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ describe Findable::Associations::ActiveRecordExt do
4
+ let(:company) { Company.first }
5
+ let(:store) { Store.first }
6
+ let(:email) { Email.first }
7
+ let(:user) { User.first }
8
+
9
+ describe "#has_many" do
10
+ it { expect(company.users).to be_kind_of(Array) }
11
+ it { expect(company.users.first).to be_kind_of(User) }
12
+ it { expect(company.stores).to be_kind_of(ActiveRecord::Relation) }
13
+ it { expect(company.stores.first).to be_kind_of(Store) }
14
+ end
15
+
16
+ describe "#has_one" do
17
+ it { expect(company.image).to be_kind_of(Image) }
18
+ it { expect(store.email).to be_kind_of(Email) }
19
+ end
20
+
21
+ describe "#belongs_to" do
22
+ it { expect(email.user).to be_kind_of(User) }
23
+ it { expect(store.company).to be_kind_of(Company) }
24
+ it {
25
+ email.user = user
26
+ expect(email.user_id).to eq(user.id)
27
+ }
28
+ end
29
+ end
30
+
@@ -4,12 +4,12 @@ describe Findable::Associations::Utils do
4
4
  let(:utils) { Findable::Associations::Utils }
5
5
 
6
6
  describe "#model_for" do
7
- include_context "FindableModels"
8
- let(:model_name) { findable_model.model_name }
7
+ include_context "TemporaryModel"
8
+ let(:model_name) { model.model_name }
9
9
 
10
- it { expect(utils.model_for(model_name.singular)).to eq(findable_model) }
11
- it { expect(utils.model_for(model_name.plural, collection: true)).to eq(findable_model) }
12
- it { expect(utils.model_for("invalid", class_name: model_name.name)).to eq(findable_model) }
10
+ it { expect(utils.model_for(model_name.singular)).to eq(model) }
11
+ it { expect(utils.model_for(model_name.plural, collection: true)).to eq(model) }
12
+ it { expect(utils.model_for("invalid", class_name: model_name.name)).to eq(model) }
13
13
 
14
14
  it { expect { utils.model_for("invalid") }.to raise_error }
15
15
  it { expect { utils.model_for("invalid", safe: true) }.not_to raise_error }
@@ -1,21 +1,34 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Findable::Associations do
4
- include_context "AssociationModels"
5
- let(:group) { Group.first }
6
- let(:info) { Info.first }
7
- let(:tag) { Tag.first }
4
+ let(:category) { Category.first }
5
+ let(:product) { Product.first }
6
+ let(:image) { Image.first }
7
+ let(:user) { User.find(UserData.first[:id]) }
8
+ let(:user2) { User.find(UserData.last[:id]) }
9
+ let(:other_company) { Company.last }
8
10
 
9
11
  describe "#has_many" do
10
- it { expect(group.tags.map(&:id).sort).to eq(Tag.all.map(&:id).sort) }
12
+ it { expect(category.products).to be_kind_of(Array) }
13
+ it { expect(category.products.first).to be_kind_of(Product) }
14
+ it { expect(user.pictures).to be_kind_of(ActiveRecord::Relation) }
15
+ it { expect(user.pictures.first).to be_kind_of(Picture) }
11
16
  end
12
17
 
13
18
  describe "#has_one" do
14
- it { expect(group.info.id).to eq(info.id) }
19
+ it { expect(product.image).to be_kind_of(Image) }
20
+ it { expect(user.email).to be_kind_of(Email) }
15
21
  end
16
22
 
17
23
  describe "#belongs_to" do
18
- it { expect(tag.group.id).to eq(group.id) }
24
+ it { expect(product.category).to be_kind_of(Category) }
25
+ it { expect(user.company).to be_kind_of(Company) }
26
+ it { expect(user.content).to be_kind_of(Image) }
27
+ it { expect(user2.content).to be_kind_of(Picture) }
28
+ it {
29
+ user.company = other_company
30
+ expect(user).to have_attributes(company_id: other_company.id)
31
+ }
19
32
  end
20
33
  end
21
34
 
@@ -1,5 +1,75 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Findable::Base do
4
+ include_context "TemporaryModel"
5
+ include_context "ReadOnlyModel"
6
+
7
+ describe ".primary_key" do
8
+ it { expect(model.primary_key).to eq("id") }
9
+ end
10
+
11
+ describe ".column_names" do
12
+ it { expect(model.column_names).to eq([:id, :name]) }
13
+ end
14
+
15
+ describe ".all" do
16
+ it { expect(read_model.all).to be_kind_of(Array) }
17
+ it { expect(read_model.all.size).to eq(1) }
18
+ end
19
+
20
+ describe ".find" do
21
+ it { expect(read_model.find(id)).to be_kind_of(read_model) }
22
+ it { expect(read_model.find([id])).to be_kind_of(Array) }
23
+ it {
24
+ expect {
25
+ read_model.find(invalid_id)
26
+ }.to raise_error(Findable::RecordNotFound)
27
+ }
28
+ end
29
+
30
+ describe ".find_by" do
31
+ it { expect(read_model.find_by(id: id)).to be_kind_of(read_model) }
32
+ it { expect(read_model.find_by(id: invalid_id)).to be_nil }
33
+ it { expect(read_model.find_by(id: id, name: name)).to be_kind_of(read_model) }
34
+ it { expect(read_model.find_by(id: id, name: invalid_name)).to be_nil }
35
+ it { expect(read_model.find_by(name: name)).to be_kind_of(read_model) }
36
+ it { expect(read_model.find_by(name: invalid_name)).to be_nil }
37
+ it { expect(read_model.find_by(id)).to be_kind_of(read_model) }
38
+ it { expect(read_model.find_by(invalid_id)).to be_nil }
39
+ end
40
+
41
+ describe ".find_by!" do
42
+ it {
43
+ expect {
44
+ read_model.find_by!(id: id)
45
+ }.not_to raise_error
46
+ }
47
+ it {
48
+ expect {
49
+ read_model.find_by!(id: invalid_id)
50
+ }.to raise_error(Findable::RecordNotFound)
51
+ }
52
+ end
53
+
54
+ describe ".where" do
55
+ it { expect(read_model.where(id: id)).to be_kind_of(Array) }
56
+ it { expect(read_model.where(id: id).first).to be_kind_of(read_model) }
57
+ it { expect(read_model.where(id: invalid_id)).to be_empty }
58
+ it { expect(read_model.where(id: id, name: name)).to be_kind_of(Array) }
59
+ it { expect(read_model.where(id: id, name: name).first).to be_kind_of(read_model) }
60
+ it { expect(read_model.where(id: invalid_id, name: name)).to be_empty }
61
+ it { expect(read_model.where(name: name)).to be_kind_of(Array) }
62
+ it { expect(read_model.where(name: name).first).to be_kind_of(read_model) }
63
+ it { expect(read_model.where(name: invalid_name)).to be_empty }
64
+ end
65
+
66
+ describe ".create" do
67
+ it {
68
+ expect {
69
+ model.create(name: "example")
70
+ }.to change { model.count }.by(1)
71
+ }
72
+ it { expect(model).to respond_to(:create!) }
73
+ end
4
74
  end
5
75
 
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe "Errors" do
4
+ include_context "ReadOnlyModel"
5
+
6
+ describe Findable::FindableError do
7
+ it { is_expected.to be_kind_of(StandardError) }
8
+ end
9
+
10
+ describe Findable::RecordNotFound do
11
+ let(:error) { Findable::RecordNotFound.new(read_model, {id: 1}) }
12
+ it { expect(error).to be_kind_of(Findable::FindableError) }
13
+ it { expect(error.message).to match(Regexp.new(read_model.model_name.name)) }
14
+ it { expect(error.message).to match(Regexp.new({id: 1}.inspect)) }
15
+ end
16
+ end
17
+
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ describe Findable::Query::Connection do
4
+ before do
5
+ class QueryEx; end
6
+ QueryEx.include Findable::Query::Connection
7
+ end
8
+
9
+ after do
10
+ Object.send(:remove_const, "QueryEx")
11
+ end
12
+
13
+ let(:query) { QueryEx.new }
14
+ let(:custom_options) { {host: "localhost", port: 6379, db: 15} }
15
+
16
+ describe "#redis" do
17
+ it { expect(query.redis).to be_kind_of(Redis) }
18
+ it {
19
+ expect(query).to receive(:generate_redis_connection!)
20
+ query.redis
21
+ }
22
+ end
23
+
24
+ describe "#generate_redis_connection!" do
25
+ context "when redis_options is nil" do
26
+ before { Findable.config.redis_options = nil }
27
+
28
+ it { expect(query.send(:generate_redis_connection!)).to be_kind_of(Redis) }
29
+ it {
30
+ expect(Redis).not_to receive(:new)
31
+ expect(Redis).to receive(:current)
32
+ query.redis
33
+ }
34
+ end
35
+
36
+ context "when redis_options is not nil" do
37
+ before { Findable.config.redis_options = custom_options }
38
+
39
+ it { expect(query.send(:generate_redis_connection!)).to be_kind_of(Redis) }
40
+ it {
41
+ expect(Redis).to receive(:new)
42
+ expect(Redis).not_to receive(:current)
43
+ query.redis
44
+ }
45
+ end
46
+ end
47
+
48
+ describe "#redis_options" do
49
+ context "with custom options" do
50
+ it {
51
+ Findable.config.redis_options = custom_options
52
+ expect(query.send(:redis_options)).to eq(custom_options)
53
+ }
54
+ end
55
+
56
+ context "without custom options" do
57
+ after { expect(query.send(:redis_options)).to be_nil }
58
+
59
+ it { Findable.config.redis_options = nil }
60
+ it { Findable.config.redis_options = {} }
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ describe Findable::Query::Namespace do
4
+ include_context "TemporaryModel"
5
+
6
+ before do
7
+ QueryEx = Class.new
8
+ QueryEx.include Findable::Query::Namespace
9
+ end
10
+
11
+ after { Object.send(:remove_const, "QueryEx") }
12
+
13
+ let(:namespace) { Findable::Query::Namespace }
14
+ let(:query) { QueryEx.new(model) }
15
+
16
+ describe "Method definitions of various keys" do
17
+ it {
18
+ namespace::KEY_NAMES.each do |name|
19
+ expect(query.public_send("#{name}_key")).to be_kind_of(String)
20
+ expect(query.public_send("#{name}_key")).to start_with(namespace::PREFIX)
21
+ end
22
+ }
23
+ end
24
+
25
+ describe "#initialize" do
26
+ it { expect(query.instance_eval { @_model }).to eq(model) }
27
+ end
28
+
29
+ describe "#thread_key" do
30
+ it { expect(query.thread_key).to be_kind_of(String) }
31
+ it { expect(query.thread_key).to start_with(namespace::PREFIX) }
32
+ end
33
+
34
+ describe "#basename" do
35
+ it { expect(query.send(:basename)).to eq("sizes") }
36
+ end
37
+
38
+ describe "#namespaces" do
39
+ it { expect(query.send(:namespaces)).to be_kind_of(Hash) }
40
+ it {
41
+ expect(query.send(:namespaces)).to satisfy {|hash|
42
+ namespace::KEY_NAMES.all? {|key| hash.has_key?(key) }
43
+ }
44
+ }
45
+ it {
46
+ query.send(:namespaces)
47
+ expect(query).not_to receive(:basename)
48
+ query.send(:namespaces)
49
+ }
50
+ end
51
+ end
52
+
@@ -0,0 +1,102 @@
1
+ require "spec_helper"
2
+
3
+ describe Findable::Query do
4
+ include_context "TemporaryModel"
5
+ include_context "ReadOnlyModel"
6
+
7
+ let(:auto_increment_key) { Findable::Query::Namespace::AUTO_INCREMENT_KEY }
8
+
9
+ describe "#data" do
10
+ it { expect(read_model.query.data).to be_kind_of(Array) }
11
+ it { expect(read_model.query.data.size).to eq(1) }
12
+ end
13
+
14
+ describe "#ids" do
15
+ it { expect(read_model.query.ids).to be_kind_of(Array) }
16
+ it { expect(read_model.query.ids).to eq([1]) }
17
+ end
18
+
19
+ describe "#count" do
20
+ it { expect(read_model.query.count).to eq(1) }
21
+ end
22
+
23
+ describe "#find_by_ids" do
24
+ let(:raw_value) { read_model.query.find_by_ids(1) }
25
+ let(:loaded_value) { Oj.load(raw_value.first) }
26
+
27
+ it { expect(raw_value).to be_kind_of(Array) }
28
+ it { expect(raw_value.first).to be_kind_of(String) }
29
+ it { expect(loaded_value[:id]).to eq(1) }
30
+ end
31
+
32
+ describe "#exists?" do
33
+ it { expect(read_model.exists?(1)).to be_truthy }
34
+ it { expect(read_model.exists?(2)).to be_falsey }
35
+ end
36
+
37
+ describe "#insert" do
38
+ it { expect(model.query.insert(name: name)).to be_kind_of(Hash) }
39
+ it { expect(model.query.insert(name: name)[:id]).not_to be_nil }
40
+ it { expect(model.query.insert(name: name)[:name]).to eq(name) }
41
+ it {
42
+ expect {
43
+ model.query.insert(name: name)
44
+ }.to change { model.query.count }.by(1)
45
+ }
46
+ end
47
+
48
+ describe "#import" do
49
+ end
50
+
51
+ describe "#delete" do
52
+ let!(:params) { model.query.insert(name: name) }
53
+
54
+ it {
55
+ expect {
56
+ model.query.delete(params[:id])
57
+ }.to change { model.query.count }.by(-1)
58
+ }
59
+ end
60
+
61
+ describe "#delete_all" do
62
+ before {
63
+ model.query.insert(name: name)
64
+ model.query.delete_all
65
+ }
66
+
67
+ it { expect(model.query.redis.exists(model.query.data_key)).to be_falsey }
68
+ it { expect(model.query.redis.exists(model.query.info_key)).to be_falsey }
69
+ end
70
+
71
+ describe "#transaction" do
72
+ end
73
+
74
+ describe "#auto_incremented_id" do
75
+ context "when id is nil" do
76
+ let!(:id) { model.query.send(:auto_incremented_id, nil) }
77
+ let(:info_id) {
78
+ model.query.redis.hget(model.query.info_key, auto_increment_key).to_i
79
+ }
80
+
81
+ it { expect(id).to be_kind_of(Integer) }
82
+ it { expect(id).to eq(info_id) }
83
+ end
84
+
85
+ context "when id is not nil" do
86
+ before { model.query.redis.hset(model.query.info_key, auto_increment_key, 10) }
87
+
88
+ it {
89
+ expect_any_instance_of(Redis).not_to receive(:hset)
90
+ id = model.query.send(:auto_incremented_id, 5)
91
+ expect(id).to eq(5)
92
+ }
93
+
94
+ it {
95
+ expect_any_instance_of(Redis).to receive(:hset)
96
+ id = model.query.send(:auto_incremented_id, 15)
97
+ expect(id).to eq(15)
98
+ }
99
+ end
100
+ end
101
+ end
102
+
data/spec/spec_helper.rb CHANGED
@@ -13,12 +13,18 @@ SimpleCov.start do
13
13
  add_filter "example"
14
14
  end
15
15
 
16
+ require "bundler/setup"
17
+ require "active_record"
18
+ require "sqlite3"
16
19
  require "pry"
20
+
17
21
  require "findable"
22
+ require "findable/associations/active_record_ext"
18
23
 
19
24
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
20
25
 
21
26
  RSpec.configure do |config|
27
+ config.order = :random
22
28
  config.after(:each) { Findable.config.reset }
23
29
  end
24
30
 
@@ -0,0 +1,107 @@
1
+ # =========================
2
+ # Initialize and Migrations
3
+ # =========================
4
+
5
+ # Initialize Redis
6
+ Redis.current.flushdb
7
+
8
+ # Initialize SQLite
9
+ ActiveRecord::Base.establish_connection({
10
+ adapter: "sqlite3",
11
+ database: ":memory:",
12
+ })
13
+
14
+ ActiveRecord::Migration.create_table :companies do |t|
15
+ t.string :name
16
+ end
17
+
18
+ ActiveRecord::Migration.create_table :stores do |t|
19
+ t.string :name
20
+ t.integer :company_id
21
+ end
22
+
23
+ ActiveRecord::Migration.create_table :emails do |t|
24
+ t.string :address
25
+ t.integer :store_id
26
+ t.integer :user_id
27
+ end
28
+
29
+ ActiveRecord::Migration.create_table :pictures do |t|
30
+ t.string :name
31
+ t.integer :user_id
32
+ end
33
+
34
+ # =========================
35
+ # Model definitions
36
+ # =========================
37
+
38
+ # Model < Findable
39
+ %w(Category Product Image User).each do |class_name|
40
+ Object.const_set(class_name, Class.new(Findable::Base))
41
+ end
42
+
43
+ # Model < ActiveRecord
44
+ %w(Company Store Email Picture).each do |class_name|
45
+ Object.const_set(class_name, Class.new(ActiveRecord::Base))
46
+ end
47
+
48
+ ActiveRecord::Base.subclasses.each do |ar|
49
+ ar.include Findable::Associations::ActiveRecordExt
50
+ end
51
+
52
+ # Associations
53
+ Category.has_many :products
54
+ Product.belongs_to :category
55
+ Product.has_one :image
56
+ Image.belongs_to :product
57
+
58
+ Company.has_many :stores
59
+ Store.belongs_to :company
60
+ Store.has_one :email
61
+ Email.belongs_to :store
62
+
63
+ User.belongs_to :content, polymorphic: true
64
+
65
+ User.has_many :pictures
66
+ Picture.belongs_to :user
67
+
68
+ User.has_one :email
69
+ Email.belongs_to :user
70
+
71
+ Company.has_many :users
72
+ User.belongs_to :company
73
+
74
+ Company.has_one :image
75
+ Image.belongs_to :company
76
+
77
+ # =========================
78
+ # Data import
79
+ # =========================
80
+ CategoryData = [{id: 1, name: "Book"}]
81
+ Category.query.import(CategoryData)
82
+
83
+ ProductData = [
84
+ {id: 1, name: "book 1", category_id: 1},
85
+ {id: 2, name: "book 2", category_id: 1},
86
+ ]
87
+ Product.query.import(ProductData)
88
+
89
+ ImageData = [
90
+ {id: 1, product_id: 1, company_id: 1},
91
+ {id: 2, product_id: 2, company_id: 1},
92
+ ]
93
+ Image.query.import(ImageData)
94
+
95
+ UserData = [
96
+ {id: 1, name: "user 1", content_type: "Image", content_id: 1, company_id: 1},
97
+ {id: 2, name: "user 2", content_type: "Picture", content_id: 1, company_id: 1}
98
+ ]
99
+ User.query.import(UserData)
100
+
101
+ Company.create!(id: 1, name: "company 1")
102
+ Company.create!(id: 2, name: "company 2")
103
+ Store.create(id: 1, name: "store 1", company_id: 1)
104
+ Email.create!(id: 1, address: "findable@example.com", store_id: 1, user_id: 1)
105
+ Picture.create!(id: 1, name: "picture 1", user_id: 1)
106
+ Picture.create!(id: 2, name: "picture 2", user_id: 1)
107
+
@@ -0,0 +1,16 @@
1
+ shared_context "TemporaryModel" do
2
+ before { class Size < Findable::Base; define_field :name; end }
3
+ after { Size.delete_all; Object.send(:remove_const, "Size") }
4
+ let(:model) { Size }
5
+ let(:instance) { model.new }
6
+ end
7
+
8
+ # model for read only test
9
+ shared_context "ReadOnlyModel" do
10
+ let(:read_model) { Category }
11
+ let(:id) { CategoryData.first[:id] }
12
+ let(:invalid_id) { CategoryData.last[:id] + 1 }
13
+ let(:name) { CategoryData.first[:name] }
14
+ let(:invalid_name) { "invalid" }
15
+ end
16
+
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.1.1
4
+ version: 0.1.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-05-06 00:00:00.000000000 Z
11
+ date: 2015-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: redis-objects
42
+ name: redis
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sqlite3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: pry
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -189,6 +203,7 @@ files:
189
203
  - lib/findable/errors.rb
190
204
  - lib/findable/query.rb
191
205
  - lib/findable/query/connection.rb
206
+ - lib/findable/query/lock.rb
192
207
  - lib/findable/query/namespace.rb
193
208
  - lib/findable/railtie.rb
194
209
  - lib/findable/seed.rb
@@ -198,15 +213,18 @@ files:
198
213
  - lib/generators/findable/templates/seeds.rb
199
214
  - lib/tasks/findable.rake
200
215
  - spec/active_model_lint_spec.rb
201
- - spec/findable/.keep
216
+ - spec/findable/associations/active_record_ext_spec.rb
202
217
  - spec/findable/associations/utils_spec.rb
203
218
  - spec/findable/associations_spec.rb
204
219
  - spec/findable/base_spec.rb
205
220
  - spec/findable/configuration_spec.rb
221
+ - spec/findable/errors_spec.rb
222
+ - spec/findable/query/connection_spec.rb
223
+ - spec/findable/query/namespace_spec.rb
224
+ - spec/findable/query_spec.rb
206
225
  - spec/spec_helper.rb
207
- - spec/support/.keep
208
- - spec/support/initialize_redis.rb
209
- - spec/support/shard_contexts.rb
226
+ - spec/support/initialize_database.rb
227
+ - spec/support/shared_contexts.rb
210
228
  homepage: https://github.com/i2bskn/findable
211
229
  licenses:
212
230
  - MIT
@@ -233,12 +251,15 @@ specification_version: 4
233
251
  summary: Redis wrapper with API like ActiveRecord.
234
252
  test_files:
235
253
  - spec/active_model_lint_spec.rb
236
- - spec/findable/.keep
254
+ - spec/findable/associations/active_record_ext_spec.rb
237
255
  - spec/findable/associations/utils_spec.rb
238
256
  - spec/findable/associations_spec.rb
239
257
  - spec/findable/base_spec.rb
240
258
  - spec/findable/configuration_spec.rb
259
+ - spec/findable/errors_spec.rb
260
+ - spec/findable/query/connection_spec.rb
261
+ - spec/findable/query/namespace_spec.rb
262
+ - spec/findable/query_spec.rb
241
263
  - spec/spec_helper.rb
242
- - spec/support/.keep
243
- - spec/support/initialize_redis.rb
244
- - spec/support/shard_contexts.rb
264
+ - spec/support/initialize_database.rb
265
+ - spec/support/shared_contexts.rb
data/spec/findable/.keep DELETED
File without changes
data/spec/support/.keep DELETED
File without changes
@@ -1,2 +0,0 @@
1
- Redis.current.flushdb
2
-
@@ -1,29 +0,0 @@
1
- shared_context "FindableModels" do
2
- before { class Tag < Findable::Base; end }
3
- after { Object.send(:remove_const, "Tag") }
4
-
5
- let(:findable_model) { Tag }
6
- let(:findable_instance) { Tag.new }
7
- end
8
-
9
- shared_context "AssociationModels" do
10
- before do
11
- class Group < Findable::Base; end
12
- class Tag < Findable::Base; end
13
- class Info < Findable::Base; end
14
- Group.has_many :tags
15
- Group.has_one :info
16
- Tag.belongs_to :group
17
- Info.belongs_to :group
18
-
19
- Group.query.import([{id: 1, name: "group1"}])
20
- Tag.query.import([
21
- {id: 1, name: "tag1", group_id: 1},
22
- {id: 2, name: "tag2", group_id: 1},
23
- ])
24
- Info.query.import([{id: 1, group_id: 1}])
25
- end
26
-
27
- after { %w(Group Tag Info).each {|name| Object.send(:remove_const, name) } }
28
- end
29
-