findable 0.1.1 → 0.1.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 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
-