nexter 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 +4 -4
- data/.pryrc +2 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/README.md +50 -4
- data/lib/nexter/version.rb +1 -1
- data/lib/nexter/wrap.rb +100 -0
- data/nexter.gemspec +2 -2
- data/spec/book.rb +37 -0
- data/spec/nexter/wrap_spec.rb +40 -0
- data/spec/spec_helper.rb +22 -0
- metadata +16 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18207b8538b39dd309f1481c2b69fc8b50261cef
|
4
|
+
data.tar.gz: ee8714526f27d7653ff607833daf07070491feb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58c0c99e1ef15ebc00c9c1534c09bf68ca7dfb51d8b491c53998ac167a054d35de0a3cefb2b3bcc34925260c33bcad683701ba814a0c418c2ad72538770b4bf3
|
7
|
+
data.tar.gz: 24c2d7d37d54e2f9d62e4eed0d7766ca5e64705a4c3f61ab544def97fddeaa125f36fa884d6f6f35180cc4f8d780b93b263f4fe3a47ecd9e4b78f07cb7a23118
|
data/.pryrc
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Nexter
|
2
2
|
|
3
|
-
What is Nexter ? A misspelled tv show or a killer feature ?
|
3
|
+
What is Nexter ? A misspelled tv show or a killer feature ? Not sure but it wraps your ActiveRecord model with an ordered scope and consistently cuts out the _next_ and _previous_ records. It also works with associations & nested columns : `order("books.genre, authors.name, published_at desc")`
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -9,13 +9,59 @@ What is Nexter ? A misspelled tv show or a killer feature ? Almost : it wraps yo
|
|
9
9
|
## Usage
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
@
|
13
|
-
|
12
|
+
@books = Book.includes(:author).bestsellers.
|
13
|
+
order("genre", "authors.name", "published_at desc")
|
14
14
|
|
15
|
-
nexter.
|
15
|
+
nexter = Nexter.wrap( @books.find(56), @books )
|
16
16
|
nexter.previous
|
17
|
+
nexter.next
|
17
18
|
```
|
18
19
|
|
20
|
+
## Use Case Full Stack
|
21
|
+
|
22
|
+
It helps you cycle consistentely through each record of any filtered collection instead of helplessly hit the back button of your browser to find the next item of your search. It plays well with gem which keeps the state of an `ActiveRelation like [siphon](https://github.com/charly/siphon), [ransack](https://github.com/activerecord-hackery/ransack), [has_scope](https://github.com/plataformatec/has_scope) & others.
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class Book
|
26
|
+
def nexter=(relation)
|
27
|
+
@nexter = Nexter.wrap(relation, self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def next
|
31
|
+
@nexter.next || self
|
32
|
+
end
|
33
|
+
|
34
|
+
def previous
|
35
|
+
@nexter.previous || self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class BookController
|
42
|
+
before_filter :resource, except: :index
|
43
|
+
|
44
|
+
def resource
|
45
|
+
@book_search = BookSearch.new(params[:book_search])
|
46
|
+
|
47
|
+
@book ||= Book.includes([:author]).find(params[:id]).tap do |book|
|
48
|
+
book.nexter = siphon(Book.scoped).scope(@book_search)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
```erb
|
55
|
+
<%= link_to "previous", book_path(@book.previous, book_search: params[:book_search]) %>
|
56
|
+
<%= link_to "collection", book_path(book_search: params[:book_search]) %>
|
57
|
+
<%= link_to "next", book_path(@book.next, book_search: params[:book_search])
|
58
|
+
```
|
59
|
+
|
60
|
+
## TODO
|
61
|
+
|
62
|
+
- (docs) How it works
|
63
|
+
- (feature) Joins ?
|
64
|
+
- (docs) previous/next through ctrl (not preloaded)
|
19
65
|
|
20
66
|
## Contributing
|
21
67
|
|
data/lib/nexter/version.rb
CHANGED
data/lib/nexter/wrap.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Nexter
|
2
|
+
class Wrap
|
3
|
+
|
4
|
+
attr_reader :model, :relation
|
5
|
+
|
6
|
+
# extracted values from the relation
|
7
|
+
attr_reader :order_values, :associations
|
8
|
+
|
9
|
+
# list of build strings for finale SQL
|
10
|
+
attr_reader :wheres, :reorders
|
11
|
+
|
12
|
+
DIREC = {asc: 1, desc: -1}
|
13
|
+
GOTO = {next: 1, previous: -1}
|
14
|
+
|
15
|
+
def initialize(relation, model)
|
16
|
+
@relation = relation
|
17
|
+
@model = model
|
18
|
+
@order_values = arrayize( relation.order_values )
|
19
|
+
@associations = relation.includes_values
|
20
|
+
# @cursor_column = extract_attr( @ranges.pop )
|
21
|
+
# @cursor = model.send( @cursor_column.to_sym, )
|
22
|
+
end
|
23
|
+
|
24
|
+
def next
|
25
|
+
after.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous
|
29
|
+
before.first
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def after
|
34
|
+
cut(:next)
|
35
|
+
relation.where( wheres.join(' OR ') )
|
36
|
+
end
|
37
|
+
|
38
|
+
def before
|
39
|
+
cut(:previous)
|
40
|
+
relation.where( wheres.join(' OR ') ).reorder( reorders.join(", ") )
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
# model.order(a, b,c) loop
|
45
|
+
# 1. ( (a and b and c > c.val)
|
46
|
+
# 2. (a and b > b.val)
|
47
|
+
# 3. (a > a.val))
|
48
|
+
def cut(goto = :next)
|
49
|
+
order_vals = @order_values.dup
|
50
|
+
@wheres = []
|
51
|
+
@reorders = []
|
52
|
+
|
53
|
+
while order_col = order_vals.pop do
|
54
|
+
|
55
|
+
inrange_of = order_vals.map {|col| "#{col[0]} = '#{value_of(col[0])}'"}.join(' AND ')
|
56
|
+
inrange_of = "#{inrange_of} AND" unless inrange_of.blank?
|
57
|
+
|
58
|
+
direction = order_col[1]
|
59
|
+
sign = signature(direction, goto)
|
60
|
+
|
61
|
+
bigger_than = "#{order_col[0]} #{get_bracket(sign)} '#{value_of(order_col[0])}'"
|
62
|
+
|
63
|
+
wheres << "( #{inrange_of} #{bigger_than} )"
|
64
|
+
reorders.unshift(" #{order_col[0]} #{get_direction(sign)}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def signature(dir, goto)
|
69
|
+
sign = DIREC[dir.to_sym] * GOTO[goto]
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_bracket(sign)
|
73
|
+
sign == -1 ? '<' : '>'
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_direction(sign)
|
77
|
+
sign == -1 ? 'desc' : 'asc'
|
78
|
+
end
|
79
|
+
|
80
|
+
def value_of(cursor)
|
81
|
+
splits = cursor.split(".")
|
82
|
+
if splits.first == model.class.table_name || splits.size == 1
|
83
|
+
model.send(splits.last)
|
84
|
+
else
|
85
|
+
asso = model.reflections.keys.grep(/#{splits.first.singularize}/).first
|
86
|
+
model.send(asso).send(splits.last)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# helper to turn mixed order attributes to a consistant
|
91
|
+
def arrayize(array)
|
92
|
+
array.join(",").split(",").map(&:strip).map do |column|
|
93
|
+
splits = column.split(" ").map(&:strip).map(&:downcase)
|
94
|
+
splits << "asc" if splits.size == 1
|
95
|
+
splits
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
data/nexter.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Nexter::VERSION
|
9
9
|
spec.authors = ["Charles Sistovaris"]
|
10
10
|
spec.email = ["charlysisto@gmail.com"]
|
11
|
-
spec.description = %q{What is Nexter ? A misspelled tv show or a killer feature ? Almost : it wraps your model with an ordered scope and cuts out the
|
11
|
+
spec.description = %q{What is Nexter ? A misspelled tv show or a killer feature ? Almost : it wraps your model with an ordered scope and cuts out the next and previous record. It also works with associations & nested columns.}
|
12
12
|
spec.summary = %q{Wrap your model with an ordered scope and cut out the _next_ and _previous_ record.}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/charly/nexter"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
data/spec/book.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Author = Struct.new(:b)
|
2
|
+
|
3
|
+
Book = Struct.new(:a, :b, :c) do
|
4
|
+
|
5
|
+
def self.table_name
|
6
|
+
"books"
|
7
|
+
end
|
8
|
+
|
9
|
+
def reflections
|
10
|
+
{:author => "blurb"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def author
|
14
|
+
Author.new("nabokov")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Relation
|
19
|
+
attr_accessor :order_values, :includes_values
|
20
|
+
|
21
|
+
def order_values
|
22
|
+
@order_values ||= ["books.a", "authors.b", "books.c"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def includes_values
|
26
|
+
[:author]
|
27
|
+
end
|
28
|
+
|
29
|
+
def where(args)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def reorder(args)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Nexter::Wrap do
|
4
|
+
|
5
|
+
describe "#wheres" do
|
6
|
+
it "has the right SQL condition" do
|
7
|
+
relation = Relation.new.tap {|rel| rel.order_values=["authors.b", "c"]}
|
8
|
+
book = Book.new("novel", "nabokov", "ada")
|
9
|
+
nexter = Nexter::Wrap.new(relation, book)
|
10
|
+
nexter.after
|
11
|
+
|
12
|
+
expect(nexter.wheres[0]).to eq("( authors.b = 'nabokov' AND c > 'ada' )")
|
13
|
+
expect(nexter.wheres[1]).to eq("( authors.b > 'nabokov' )")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "#reorders" do
|
19
|
+
it "has the right SQL condition" do
|
20
|
+
relation = Relation.new.tap {|rel| rel.order_values=["authors.b", "c"]}
|
21
|
+
book = Book.new("novel", "nabokov", "ada")
|
22
|
+
nexter = Nexter::Wrap.new(relation, book)
|
23
|
+
nexter.before
|
24
|
+
|
25
|
+
expect(nexter.wheres[0]).to eq("( authors.b = 'nabokov' AND c < 'ada' )")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has the right SQL order by" do
|
29
|
+
relation = Relation.new.tap {|rel| rel.order_values=["authors.b", "c"]}
|
30
|
+
book = Book.new("novel", "nabokov", "ada")
|
31
|
+
nexter = Nexter::Wrap.new(relation, book)
|
32
|
+
nexter.before
|
33
|
+
|
34
|
+
expect(nexter.reorders[0]).to eq(" authors.b desc")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
require "book"
|
8
|
+
require "nexter"
|
9
|
+
require "active_support"
|
10
|
+
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
|
17
|
+
# Run specs in random order to surface order dependencies. If you find an
|
18
|
+
# order dependency and want to debug it, you can fix the order by providing
|
19
|
+
# the seed, which is printed after each run.
|
20
|
+
# --seed 1234
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexter
|
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
|
- Charles Sistovaris
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -95,8 +95,8 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: 'What is Nexter ? A misspelled tv show or a killer feature ? Almost :
|
98
|
-
it wraps your model with an ordered scope and cuts out the
|
99
|
-
|
98
|
+
it wraps your model with an ordered scope and cuts out the next and previous record.
|
99
|
+
It also works with associations & nested columns.'
|
100
100
|
email:
|
101
101
|
- charlysisto@gmail.com
|
102
102
|
executables: []
|
@@ -104,14 +104,21 @@ extensions: []
|
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
106
|
- .gitignore
|
107
|
+
- .pryrc
|
108
|
+
- .rspec
|
109
|
+
- .ruby-version
|
107
110
|
- Gemfile
|
108
111
|
- LICENSE.txt
|
109
112
|
- README.md
|
110
113
|
- Rakefile
|
111
114
|
- lib/nexter.rb
|
112
115
|
- lib/nexter/version.rb
|
116
|
+
- lib/nexter/wrap.rb
|
113
117
|
- nexter.gemspec
|
114
|
-
|
118
|
+
- spec/book.rb
|
119
|
+
- spec/nexter/wrap_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
homepage: https://github.com/charly/nexter
|
115
122
|
licenses:
|
116
123
|
- MIT
|
117
124
|
metadata: {}
|
@@ -136,5 +143,8 @@ signing_key:
|
|
136
143
|
specification_version: 4
|
137
144
|
summary: Wrap your model with an ordered scope and cut out the _next_ and _previous_
|
138
145
|
record.
|
139
|
-
test_files:
|
146
|
+
test_files:
|
147
|
+
- spec/book.rb
|
148
|
+
- spec/nexter/wrap_spec.rb
|
149
|
+
- spec/spec_helper.rb
|
140
150
|
has_rdoc:
|