nevermind 0.0.1 → 0.0.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/nevermind.rb +1 -1
- data/lib/nevermind/proxy.rb +154 -0
- data/lib/nevermind/version.rb +1 -1
- data/nevermind.gemspec +1 -1
- data/spec/nevermind_proxy_spec.rb +150 -0
- data/spec/schema.rb +2 -0
- data/spec/seed.rb +3 -3
- metadata +6 -8
- data/lib/nevermind/proto.rb +0 -41
- data/lib/nevermind/relation.rb +0 -5
- data/spec/nevermind_proto_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fcf26ca60bf55a6350a343e9ccc5baf4cb7ac72
|
4
|
+
data.tar.gz: 18869cda8433a822bab32a0feda6c3909c024d4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66bef2ea482fafca3162490ad6691c8b4c47aaf4787ce9619021bc525bbba4ed908df00b4aa900b92132d5af5d9a7d147c80e8d921a9ebd8cfcc3aa2fb136d4c
|
7
|
+
data.tar.gz: a20a4b61d8845474d248eb40fbc905413a8718cb09f4b17efd6d73be16c418d9aa8d7cdf4d968a9ea52390b801c8cd61c1866b003bfd3107d251b2c40eb6db94
|
data/lib/nevermind.rb
CHANGED
@@ -0,0 +1,154 @@
|
|
1
|
+
module Nevermind
|
2
|
+
class Proxy
|
3
|
+
# include Enumerable
|
4
|
+
|
5
|
+
def initialize(first, second)
|
6
|
+
@first, @second = first, second
|
7
|
+
|
8
|
+
@order_params = nil
|
9
|
+
@limit_params = nil
|
10
|
+
@offset_params = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
if @order_params || @limit_params || @offset_params
|
15
|
+
get_scoped &block
|
16
|
+
else
|
17
|
+
@first.each { |obj| yield obj }
|
18
|
+
@second.each { |obj| yield obj }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](index)
|
23
|
+
#todo: optimize!
|
24
|
+
count = 0
|
25
|
+
self.each do |obj|
|
26
|
+
return obj if count == index
|
27
|
+
count+= 1
|
28
|
+
end
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def count
|
33
|
+
if @order_params || @limit_params || @offset_params
|
34
|
+
count = 0
|
35
|
+
get_scoped { |val| count+= 1 }
|
36
|
+
return count
|
37
|
+
else
|
38
|
+
@first.count + @second.count
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def order(*args)
|
43
|
+
@order_params = *args
|
44
|
+
return self.clone
|
45
|
+
end
|
46
|
+
|
47
|
+
def limit(*args)
|
48
|
+
@limit_params = *args
|
49
|
+
return self.clone
|
50
|
+
end
|
51
|
+
|
52
|
+
def offset(*args)
|
53
|
+
@offset_params = *args
|
54
|
+
return self.clone
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(method, *args, &block)
|
58
|
+
if [:all, :where, :order].include? method
|
59
|
+
call_relation_method(method, *args, &block)
|
60
|
+
else
|
61
|
+
if '!' == method.to_s.last
|
62
|
+
call_forced_method(method, *args, &block)
|
63
|
+
else
|
64
|
+
call_regular_method(method, *args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# find_by, first, etc
|
71
|
+
def call_regular_method(method, *args, &block)
|
72
|
+
found = @first.send method, *args, &block
|
73
|
+
if found.nil?
|
74
|
+
found = @second.send method, *args, &block
|
75
|
+
end
|
76
|
+
return found
|
77
|
+
end
|
78
|
+
|
79
|
+
# find_by!, first!, etc
|
80
|
+
def call_forced_method(method, *args, &block)
|
81
|
+
begin
|
82
|
+
found = @first.send method, *args, &block
|
83
|
+
rescue
|
84
|
+
found = @second.send method, *args, &block
|
85
|
+
ensure
|
86
|
+
return found
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def call_relation_method(method, *args, &block)
|
91
|
+
Proxy.new @first.send(method, *args, &block),
|
92
|
+
@second.send(method, *args, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
# yields scoped result
|
96
|
+
def get_scoped(&block)
|
97
|
+
# can be used only with ActiveRecord
|
98
|
+
#todo: implement Mongoid
|
99
|
+
unless defined?(ActiveRecord::Relation) && @first.is_a?(ActiveRecord::Relation) && @second.is_a?(ActiveRecord::Relation)
|
100
|
+
raise "#{ self.class }.#{ __method__ } not implemented for #{ @first.class } && #{ @second.class } yet. Only ActiveRecord::Relation i supported"
|
101
|
+
end
|
102
|
+
|
103
|
+
get_scoped_active_record &block
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_scoped_active_record(&block)
|
107
|
+
# get records ids in the desired scope
|
108
|
+
# prepare sql
|
109
|
+
#todo: protect from sql injection
|
110
|
+
tables = {@first.table_name => @first,
|
111
|
+
@second.table_name => @second}
|
112
|
+
|
113
|
+
order_by = @order_params.map do |param|
|
114
|
+
if param.is_a? Hash
|
115
|
+
"#{ param.first[0] } #{ param.first[1].upcase }"
|
116
|
+
elsif param.is_a? String
|
117
|
+
param
|
118
|
+
elsif param.is_a? Symbol
|
119
|
+
param.to_s
|
120
|
+
end
|
121
|
+
end.join(', ')
|
122
|
+
|
123
|
+
columns = (['id'] +
|
124
|
+
@order_params.map do |param|
|
125
|
+
if param.is_a? Hash
|
126
|
+
param.first[0].to_s
|
127
|
+
elsif param.is_a? String
|
128
|
+
param
|
129
|
+
elsif param.is_a? Symbol
|
130
|
+
param.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
).join(', ')
|
134
|
+
|
135
|
+
sql = "SELECT #{ columns }, '#{ tables.keys[0] }' as table_name FROM #{ tables.keys[0] } as `a`" +
|
136
|
+
' UNION' +
|
137
|
+
" SELECT #{ columns }, '#{ tables.keys[1] }' as table_name FROM #{ tables.keys[1] } as `b`" +
|
138
|
+
(order_by == '' ? '' : " ORDER BY #{ order_by }") +
|
139
|
+
if @limit_params.nil?
|
140
|
+
''
|
141
|
+
else
|
142
|
+
" LIMIT #{ @offset_params.nil? ? '' : "#{ @offset_params[0].to_s }, " }#{ @limit_params[0].to_s }"
|
143
|
+
end
|
144
|
+
|
145
|
+
# execute prepared sql
|
146
|
+
records_array = ActiveRecord::Base.connection.execute(sql)
|
147
|
+
|
148
|
+
# get records in the desired scope
|
149
|
+
records_array.each do |r|
|
150
|
+
yield tables[r['table_name']].find(r['id'])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/nevermind/version.rb
CHANGED
data/nevermind.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Roman Exempliarov"]
|
10
10
|
spec.email = ["urvala@gmail.com"]
|
11
11
|
spec.summary = %q{Abstraction layer that allows to work with multiple models just like with one.}
|
12
|
-
spec.description = %q{In development now.
|
12
|
+
spec.description = %q{In development now.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Nevermind::Proxy do
|
4
|
+
before :all do
|
5
|
+
@posts = Nevermind::Proxy.new(Article.all, Video.all)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'handles .find_by & .find_by!' do
|
9
|
+
[
|
10
|
+
[Article, @posts.find_by!(content: 'article')],
|
11
|
+
[Video, @posts.find_by!(content: 'video')],
|
12
|
+
[Article, @posts.find_by(content: 'article')],
|
13
|
+
[Video, @posts.find_by(content: 'video')]
|
14
|
+
].each do |klass, post|
|
15
|
+
expect(post.class).to eq klass
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'handles .first' do
|
20
|
+
post = @posts.first
|
21
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles .first!' do
|
25
|
+
post = @posts.first!
|
26
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'handles .where' do
|
30
|
+
[
|
31
|
+
[ Article, @posts.where(content: 'article') ],
|
32
|
+
[ Video, @posts.where(content: 'video') ]
|
33
|
+
].each do |klass, posts|
|
34
|
+
expect(posts.class).to eq Nevermind::Proxy
|
35
|
+
post = posts.first
|
36
|
+
expect(post.class).to eq klass
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles .all' do
|
41
|
+
posts = @posts.all
|
42
|
+
expect(posts.class).to eq Nevermind::Proxy
|
43
|
+
post = posts.first
|
44
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'handles .each' do
|
48
|
+
vid = 0
|
49
|
+
count = 0
|
50
|
+
vid_expectation = Article.all.map(&:vid).reduce(&:+) + Video.all.map(&:vid).reduce(&:+)
|
51
|
+
count_expectation = Article.count + Video.count
|
52
|
+
@posts.each { |post| vid+= post.vid; count+= 1 }
|
53
|
+
expect(count).to eq (count_expectation)
|
54
|
+
expect(vid).to eq (vid_expectation)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'handles .count' do
|
58
|
+
count = Article.all.count + Video.all.count
|
59
|
+
expect(@posts.count).to eq count
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'handles .order with single param' do
|
63
|
+
posts = @posts.order :rand
|
64
|
+
previous_post = false
|
65
|
+
expect( posts.count ).to eq Article.count + Video.count
|
66
|
+
posts.each do |post|
|
67
|
+
unless false == previous_post
|
68
|
+
expect(post.rand).to be >= previous_post.rand
|
69
|
+
end
|
70
|
+
previous_post = post
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'handles .order with single desc param' do
|
75
|
+
posts = @posts.order :rand => :desc
|
76
|
+
previous_post = false
|
77
|
+
expect(posts.count).to eq Article.count + Video.count
|
78
|
+
posts.each do |post|
|
79
|
+
unless false == previous_post
|
80
|
+
expect(post.rand).to be <= previous_post.rand
|
81
|
+
end
|
82
|
+
previous_post = post
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'handles .order with double param' do
|
87
|
+
posts = @posts.order :id, :rand => :desc
|
88
|
+
previous_post = false
|
89
|
+
expect(posts.count).to eq Article.count + Video.count
|
90
|
+
posts.each do |post|
|
91
|
+
unless false == previous_post
|
92
|
+
expect(post.id).to be >= previous_post.id
|
93
|
+
if post.id == previous_post.id
|
94
|
+
expect(post.rand).to be <= previous_post.rand
|
95
|
+
end
|
96
|
+
end
|
97
|
+
previous_post = post
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'handles .limit' do
|
102
|
+
posts = @posts.limit 5
|
103
|
+
expect(posts.count).to eq 5
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'handles []' do
|
107
|
+
posts = @posts.order(:id)
|
108
|
+
expect(posts[0].id).to eq 1
|
109
|
+
expect(posts[1].id).to eq 1
|
110
|
+
expect(posts[2].id).to eq 2
|
111
|
+
expect(posts[3].id).to eq 2
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'handles .offset' do
|
115
|
+
last_post = @posts.limit(5)[4]
|
116
|
+
posts = @posts.limit(5).offset(0)
|
117
|
+
expect(posts.count).to eq 5
|
118
|
+
expect(posts[4]).to eq last_post
|
119
|
+
|
120
|
+
posts = @posts.order(:id).limit(4).offset(2)
|
121
|
+
expect(posts[0].id).to eq 2
|
122
|
+
expect(posts[1].id).to eq 2
|
123
|
+
expect(posts[2].id).to eq 3
|
124
|
+
expect(posts[3].id).to eq 3
|
125
|
+
expect(posts[4]).to eq nil
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'handles .order with .limit & .offset' do
|
130
|
+
posts_offset_0 = @posts.order(:rand).limit(5)
|
131
|
+
posts_offset_2 = @posts.order(:rand).limit(5).offset(2)
|
132
|
+
|
133
|
+
expect(posts_offset_0.count).to eq 5
|
134
|
+
expect(posts_offset_2.count).to eq 5
|
135
|
+
|
136
|
+
[posts_offset_0, posts_offset_2].each do |posts|
|
137
|
+
previous_post = false
|
138
|
+
posts.each do |post|
|
139
|
+
unless false == previous_post
|
140
|
+
expect(post.rand).to be >= previous_post.rand
|
141
|
+
end
|
142
|
+
previous_post = post
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
[0,1,2].each do |i|
|
147
|
+
expect(posts_offset_2[i]).to be == posts_offset_0[i+2]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/spec/schema.rb
CHANGED
@@ -3,6 +3,7 @@ ActiveRecord::Schema.define do
|
|
3
3
|
|
4
4
|
create_table :articles, :force => true do |t|
|
5
5
|
t.integer :vid
|
6
|
+
t.integer :rand
|
6
7
|
t.string :content
|
7
8
|
|
8
9
|
t.timestamps
|
@@ -10,6 +11,7 @@ ActiveRecord::Schema.define do
|
|
10
11
|
|
11
12
|
create_table :videos, :force => true do |t|
|
12
13
|
t.integer :vid
|
14
|
+
t.integer :rand
|
13
15
|
t.string :content
|
14
16
|
|
15
17
|
t.timestamps
|
data/spec/seed.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
2
|
-
Article.create vid: i*2, content: 'article'
|
3
|
-
Video.create vid: i*2+1, content: 'video'
|
1
|
+
20.times do |i|
|
2
|
+
Article.create vid: i*2, content: 'article', rand: rand(1000)
|
3
|
+
Video.create vid: i*2+1, content: 'video', rand: rand(1000)
|
4
4
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nevermind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Exempliarov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,8 +80,7 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
84
|
-
find_by. '
|
83
|
+
description: In development now.
|
85
84
|
email:
|
86
85
|
- urvala@gmail.com
|
87
86
|
executables: []
|
@@ -94,12 +93,11 @@ files:
|
|
94
93
|
- README.md
|
95
94
|
- Rakefile
|
96
95
|
- lib/nevermind.rb
|
97
|
-
- lib/nevermind/
|
98
|
-
- lib/nevermind/relation.rb
|
96
|
+
- lib/nevermind/proxy.rb
|
99
97
|
- lib/nevermind/version.rb
|
100
98
|
- nevermind.gemspec
|
101
99
|
- spec/models.rb
|
102
|
-
- spec/
|
100
|
+
- spec/nevermind_proxy_spec.rb
|
103
101
|
- spec/schema.rb
|
104
102
|
- spec/seed.rb
|
105
103
|
- spec/spec_helper.rb
|
@@ -130,7 +128,7 @@ summary: Abstraction layer that allows to work with multiple models just like wi
|
|
130
128
|
one.
|
131
129
|
test_files:
|
132
130
|
- spec/models.rb
|
133
|
-
- spec/
|
131
|
+
- spec/nevermind_proxy_spec.rb
|
134
132
|
- spec/schema.rb
|
135
133
|
- spec/seed.rb
|
136
134
|
- spec/spec_helper.rb
|
data/lib/nevermind/proto.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Nevermind
|
2
|
-
class Proto
|
3
|
-
def initialize(first, second)
|
4
|
-
@first, @second = first, second
|
5
|
-
end
|
6
|
-
|
7
|
-
def where(*args)
|
8
|
-
@first.where(*args)
|
9
|
-
@second.where(*args)
|
10
|
-
end
|
11
|
-
|
12
|
-
def method_missing(method, *args, &block)
|
13
|
-
if '!' == method.to_s.last
|
14
|
-
forced_method_missing(method, *args, &block)
|
15
|
-
else
|
16
|
-
regular_method_missing(method, *args, &block)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
# find_by, first, etc
|
22
|
-
def regular_method_missing(method, *args, &block)
|
23
|
-
found = @first.send method, *args, &block
|
24
|
-
if found.nil?
|
25
|
-
found = @second.send method, *args, &block
|
26
|
-
end
|
27
|
-
return found
|
28
|
-
end
|
29
|
-
|
30
|
-
# find_by!, first!, etc
|
31
|
-
def forced_method_missing(method, *args, &block)
|
32
|
-
begin
|
33
|
-
found = @first.send method, *args, &block
|
34
|
-
rescue
|
35
|
-
found = @second.send method, *args, &block
|
36
|
-
ensure
|
37
|
-
return found
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/nevermind/relation.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe Nevermind::Proto do
|
4
|
-
it 'passes find_by' do
|
5
|
-
posts = Nevermind::Proto.new(Article, Video)
|
6
|
-
post = posts.find_by(content: 'article')
|
7
|
-
expect(post.class).to eq Article
|
8
|
-
post = posts.find_by(content: 'video')
|
9
|
-
expect(post.class).to eq Video
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'passes find_by!' do
|
13
|
-
posts = Nevermind::Proto.new(Article, Video)
|
14
|
-
post = posts.find_by!(content: 'article')
|
15
|
-
expect(post.class).to eq Article
|
16
|
-
post = posts.find_by!(content: 'video')
|
17
|
-
expect(post.class).to eq Video
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'passes first' do
|
21
|
-
posts = Nevermind::Proto.new(Article, Video)
|
22
|
-
post = posts.first
|
23
|
-
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'passes first!' do
|
27
|
-
posts = Nevermind::Proto.new(Article, Video)
|
28
|
-
post = posts.first
|
29
|
-
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
30
|
-
end
|
31
|
-
end
|