nevermind 0.0.1 → 0.0.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: 801d69d84b3ad05b15dba9d8d321c8618ab6b2ef
4
- data.tar.gz: faf22b0eb6e66da9f35c797785fa30c9d0f90b15
3
+ metadata.gz: 7fcf26ca60bf55a6350a343e9ccc5baf4cb7ac72
4
+ data.tar.gz: 18869cda8433a822bab32a0feda6c3909c024d4c
5
5
  SHA512:
6
- metadata.gz: 9cb18bf0a46718c59af0cb3d26e427b71fb1bcdd409ed3b58008f94680e96fa0d7fb22c70f391a8834ea109692ea8ccee46ca1f9d891c8f103b4988e645e4175
7
- data.tar.gz: 5ec0cbe2a1cbf2c0e1650e7ad0cf27be4d301a0946779ca3cf1fcd16d049e226eb6c74215220ebc266caef2cc8b3716c4f70c5b93ffa73490ebe158159eb9457
6
+ metadata.gz: 66bef2ea482fafca3162490ad6691c8b4c47aaf4787ce9619021bc525bbba4ed908df00b4aa900b92132d5af5d9a7d147c80e8d921a9ebd8cfcc3aa2fb136d4c
7
+ data.tar.gz: a20a4b61d8845474d248eb40fbc905413a8718cb09f4b17efd6d73be16c418d9aa8d7cdf4d968a9ea52390b801c8cd61c1866b003bfd3107d251b2c40eb6db94
@@ -1,5 +1,5 @@
1
1
  require 'nevermind/version'
2
- require 'nevermind/proto'
2
+ require 'nevermind/proxy'
3
3
 
4
4
  module Nevermind
5
5
  # Your code goes here...
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Nevermind
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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. Next milestone: 1. Handling a pair of models with find_by. }
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
@@ -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
@@ -1,4 +1,4 @@
1
- 5.times do |i|
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.1
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-17 00:00:00.000000000 Z
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: 'In development now. Next milestone: 1. Handling a pair of models with
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/proto.rb
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/nevermind_proto_spec.rb
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/nevermind_proto_spec.rb
131
+ - spec/nevermind_proxy_spec.rb
134
132
  - spec/schema.rb
135
133
  - spec/seed.rb
136
134
  - spec/spec_helper.rb
@@ -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
@@ -1,5 +0,0 @@
1
- module Nevermind
2
- class Relation
3
-
4
- end
5
- end
@@ -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