siphon 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +61 -55
- data/TODO.md +17 -0
- data/lib/siphon/adapter.rb +49 -0
- data/lib/siphon/base.rb +15 -51
- data/lib/siphon/nil.rb +12 -0
- data/lib/siphon/version.rb +1 -1
- data/lib/siphon.rb +2 -7
- data/siphon.gemspec +3 -0
- data/spec/class/formobj.rb +31 -0
- data/spec/siphon/adapter_spec.rb +43 -0
- data/spec/siphon/base_spec.rb +25 -51
- data/spec/siphon/formobj_spec.rb +94 -0
- data/spec/siphon/mapper_spec.rb +19 -0
- data/spec/siphon_spec.rb +6 -2
- data/spec/spec_helper.rb +7 -5
- metadata +41 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9cbec06f2449680deffd25cef9db8e0d217e522
|
4
|
+
data.tar.gz: c0fe656444c3e47bd1423ab40ee80afa76c58b91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96c32edf06fe03b7fdb89f2056a11da27856410cd2750800ac5c5a1978387b605f00d44024a2b88f5f281aff3fb95aa377996047bb2da0fc1d5fb21568b1b8f1
|
7
|
+
data.tar.gz: 697a795ec9c66240c83fc70135e855aeae7c631af6c2dd751d70ca74a2ed797c443c0a271f036182a458322a283af345d8ece047b053e48f576cc58af2a5503c
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Siphon
|
2
2
|
|
3
|
-
Siphon's a minimalistic gem which enables you to easily apply
|
3
|
+
Siphon's a minimalistic gem which enables you to easily and conditionnaly apply your ActiveRecord scopes with parameteres sent through the web.
|
4
|
+
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -8,81 +9,83 @@ Add this line to your application's Gemfile:
|
|
8
9
|
|
9
10
|
gem 'siphon'
|
10
11
|
|
12
|
+
|
11
13
|
## Usage
|
12
14
|
|
13
|
-
Siphon is
|
15
|
+
So Siphon's just a tiny convienience gem which is still quit experimental but it does its job of applying scopes to an activerecord model thanks to a formobject (using the great [virtus][1] here) containing the coerced values.
|
16
|
+
Here's how it works :
|
14
17
|
|
15
|
-
###
|
18
|
+
### The Scopes :
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@books ||= Siphon.new(Book.includes(:auhtor)).
|
22
|
-
has_scopes({year: integer, author_name: :string}).filter(params)
|
20
|
+
# order.rb
|
21
|
+
class Order < ActiveRecord::Base
|
22
|
+
scope :stale, ->(duration) { where(["state='onhold' OR (state != 'done' AND updated_at < ?)", duration.ago]) }
|
23
|
+
scope :unpaid -> { where(paid: false) }
|
23
24
|
end
|
24
25
|
|
25
|
-
###
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
26
|
+
### The Form :
|
27
|
+
|
28
|
+
= form_for @order_form do |f|
|
29
|
+
= f.label :stale, "Stale since more than"
|
30
|
+
= f.select :stale, [["1 week", 1.week], ["3 weeks", 3.weeks], ["3 months", 3.months]], include_blank: true
|
31
|
+
= f.label :unpaid
|
32
|
+
= f.check_box :unpaid
|
33
|
+
|
34
|
+
### The Form Object:
|
35
|
+
|
36
|
+
# order_form.rb
|
37
|
+
class OrderForm
|
38
|
+
include Virtus.model
|
39
|
+
include ActiveModel::Model
|
40
|
+
#
|
41
|
+
# attribute are the named scopes and their value are :
|
42
|
+
# - either the value you pass a scope which takes arguments
|
43
|
+
# - either a Siphon::Nil value to apply (or not) a scope which has no argument
|
44
|
+
#
|
45
|
+
attribute :stale, Integer
|
46
|
+
attribute :unpaid, Siphon::Nil
|
39
47
|
end
|
40
48
|
|
41
|
-
## Why Siphon ?
|
42
49
|
|
43
|
-
|
50
|
+
### Aaaaand... TADA siphon :
|
44
51
|
|
45
|
-
|
52
|
+
# orders_controller.rb
|
53
|
+
def search
|
54
|
+
@order_form = OrderForm.new(params[:order_form])
|
55
|
+
@orders = siphon(Order.all).scope(@order_form)
|
56
|
+
end
|
46
57
|
|
47
|
-
|
58
|
+
Here's how it works : it takes an initial ActiveRelation (Order.all) and then from a FormObject (@order_form) it will apply or not a set of scopes with typecasted arguments.
|
48
59
|
|
49
|
-
def index
|
50
|
-
@results = Results.scoped
|
51
|
-
@results = @results.gender(params[:gender]) if params[:gender]
|
52
|
-
@results = @results.career(params[:career]) if params[:career]
|
53
|
-
end
|
54
60
|
|
55
|
-
|
61
|
+
## Advanced Usage
|
56
62
|
|
57
|
-
|
63
|
+
You may want to check [how to combine siphon with ransack][2]
|
58
64
|
|
59
|
-
So what about has_scope ?
|
60
65
|
|
61
|
-
|
62
|
-
has_scope :gender
|
63
|
-
has_scope :career
|
64
|
-
|
65
|
-
def index
|
66
|
-
@results = apply_scopes(Results).all
|
67
|
-
end
|
66
|
+
## Some Insights
|
68
67
|
|
69
|
-
|
68
|
+
Siphon stands on the giant shoulder of Virtus which takes care of coercing the string values a form sends to the controller. But coercing is only solving one part of the problem. This is where siphon comes into play :
|
70
69
|
|
71
|
-
|
72
|
-
has_scope :color, :unless => :show_all_colors?
|
73
|
-
has_scope :only_tall, :type => :boolean, :only => :index, :if => :restrict_to_only_tall_trees?
|
74
|
-
has_scope :shadown_range, :default => 10, :except => [ :index, :show, :new ]
|
75
|
-
has_scope :root_type, :as => :root, :allow_blank => true
|
76
|
-
has_scope :calculate_height, :default => proc {|c| c.session[:height] || 20 }, :only => :new
|
70
|
+
As you saw in the example a scope either takes an argument(s) or it doesn't so the idea is :
|
77
71
|
|
78
|
-
|
72
|
+
1. either the field holds a value which is meant to be passed to the scope (like a date, a name, a duration, etc).
|
73
|
+
2. either the scopes takes no argument and the boolean field (like a check box) only indicates if you want it applied or not.
|
79
74
|
|
80
|
-
|
81
|
-
* You're not limited by the set of configuration the gem gives you.
|
82
|
-
* you can apply as much logic as you whish
|
83
|
-
* It's encapsultated, unit-testable & respects SRP
|
75
|
+
So the values have two distinctive roles .
|
84
76
|
|
85
|
-
|
77
|
+
In the case of `stale` which takes a duration :
|
78
|
+
|
79
|
+
= form_for @order_form do |f|
|
80
|
+
= f.label :stale, "Stale since more than"
|
81
|
+
= f.select :stale, [["1 week", 1], ["3 weeks", 3], ["9 weeks", 9]], include_blank: true
|
82
|
+
|
83
|
+
If you select a duration the scope will be called and the argument will be turned into an integer and passed as an arg.
|
84
|
+
But if you select the blank option the value of params[:stale] will be an empty string. Siphon knows it should be an integer thanks to the formobject and concludes this means no values are passed to the scope and therefore shouldn't be called.
|
85
|
+
|
86
|
+
In the case of `unpaid` siphon knows it doesn't take any argument (Siphon::Nil) and therefore only calls the scope of the field when it's not falsy ("0", "f", "false" etc).
|
87
|
+
|
88
|
+
That's all!
|
86
89
|
|
87
90
|
## Contributing
|
88
91
|
|
@@ -91,3 +94,6 @@ Actually Siphon shouln't even be a gem but just a set of guidelines. But hey it
|
|
91
94
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
92
95
|
4. Push to the branch (`git push origin my-new-feature`)
|
93
96
|
5. Create new Pull Request
|
97
|
+
|
98
|
+
[1]: https://github.com/solnic/virtus
|
99
|
+
[2]: http:://www.coderwall.com/charly
|
data/TODO.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
## TODO
|
2
|
+
* [] default param values e.g : has_scopes({ state: "pending" })
|
3
|
+
* [] play well with ransack
|
4
|
+
* [] more complex examples
|
5
|
+
|
6
|
+
## Investigate Crazy Idea
|
7
|
+
|
8
|
+
A new dsl
|
9
|
+
url: bla?published&after=1950author_lang="english"
|
10
|
+
|
11
|
+
@books = siphon(Book.includes :author)
|
12
|
+
|
13
|
+
# applies scope only if the corresponding params are present
|
14
|
+
@books.with(params).published.after(1900).author_lang("french")
|
15
|
+
# applies scope event if the params are absent but overrides argument ???
|
16
|
+
# may not be necessary : siphon(Book.order(params[:order] || "authors.birth"))
|
17
|
+
@books.ensure.instore(true).order("authors.birth")
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Siphon
|
2
|
+
# ==========
|
3
|
+
# Adapter
|
4
|
+
# ==========
|
5
|
+
#
|
6
|
+
# use : determine which scope will be called &
|
7
|
+
# set relevant args to nil for
|
8
|
+
#
|
9
|
+
#
|
10
|
+
class Adapter
|
11
|
+
|
12
|
+
def initialize(formobj)
|
13
|
+
@formobj = formobj
|
14
|
+
|
15
|
+
@scopes_hash = @formobj.scopes
|
16
|
+
@argless = argless_list(formobj)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
filterout_empty_string_and_nil
|
21
|
+
apply_nil_on_argless_scopes
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
# if scope is present in form but with no value (aka 'empty string')
|
26
|
+
# or if present in formobj but not in form (aka : value is nil)
|
27
|
+
# don't apply the scope && reject from scopes_hash
|
28
|
+
def filterout_empty_string_and_nil
|
29
|
+
@scopes_hash.delete_if { |scope, arg| ["", nil].include? @formobj[scope] }
|
30
|
+
end
|
31
|
+
|
32
|
+
# scope with no args are listed in @argless
|
33
|
+
# we loop & set their arg to nil (aka: no arg with splat)
|
34
|
+
# must be called AFTER filterout_empty_string_and_nil
|
35
|
+
def apply_nil_on_argless_scopes
|
36
|
+
@argless.each {|scope, v| @scopes_hash[scope]= nil if @scopes_hash[scope] }
|
37
|
+
@scopes_hash
|
38
|
+
end
|
39
|
+
|
40
|
+
# list of virtus attributes with Siphon::Nil type
|
41
|
+
def argless_list(formobj)
|
42
|
+
formobj.class.
|
43
|
+
attribute_set.
|
44
|
+
select {|a| a.class == Siphon::Nil}.
|
45
|
+
map(&:name)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/siphon/base.rb
CHANGED
@@ -1,66 +1,30 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
1
|
module Siphon
|
2
|
+
# ========
|
3
|
+
# Base
|
4
|
+
# ========
|
5
|
+
#
|
6
|
+
# use : Handles the ActiveRelation on which
|
7
|
+
# scopes from a **FormObject** will be applied
|
8
|
+
#
|
9
|
+
# e.g : Siphon::Base.new(Book.published).scope(BookForm.new(params[:q]))
|
10
|
+
#
|
5
11
|
class Base
|
6
|
-
attr_accessor :relation
|
7
|
-
|
12
|
+
attr_accessor :relation
|
8
13
|
|
9
14
|
# Siphon.new(Book.scoped)
|
10
15
|
def initialize(relation)
|
11
16
|
@relation = relation
|
12
17
|
end
|
13
18
|
|
14
|
-
def
|
15
|
-
|
19
|
+
def scope(formobject)
|
20
|
+
scopes_hash = Siphon::Adapter.new(formobject).call
|
16
21
|
|
17
|
-
|
18
|
-
self.relation= relation.send(
|
22
|
+
scopes_hash.each do |meth, arg|
|
23
|
+
self.relation = relation.send(meth, *arg)
|
19
24
|
end
|
20
25
|
|
21
26
|
relation
|
22
27
|
end
|
23
28
|
|
24
|
-
|
25
|
-
# -----------------
|
26
|
-
# admin: nil
|
27
|
-
# category_name: String
|
28
|
-
# published: Boolean,
|
29
|
-
# age_gt: Integer
|
30
|
-
# before: Date
|
31
|
-
#
|
32
|
-
def has_scopes(scope_datatypes = {})
|
33
|
-
@scope_datatypes = scope_datatypes.symbolize_keys
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
# TODO : for now we're assuming scope_datatypes is a Hash, K ?
|
39
|
-
def map_scope_datatypes( params, scope_datatypes )
|
40
|
-
scope_hash = {}
|
41
|
-
|
42
|
-
params.symbolize_keys.each do |scope, value|
|
43
|
-
next unless scope_datatypes.has_key?(scope)
|
44
|
-
scope_hash[scope] = convert_type( value, scope_datatypes[scope] )
|
45
|
-
end
|
46
|
-
|
47
|
-
return scope_hash
|
48
|
-
end
|
49
|
-
|
50
|
-
def convert_type(value, type)
|
51
|
-
v = case type
|
52
|
-
when :integer
|
53
|
-
Integer(value)
|
54
|
-
when :boolean
|
55
|
-
value != "false"
|
56
|
-
when :string
|
57
|
-
value
|
58
|
-
when :none
|
59
|
-
nil
|
60
|
-
else
|
61
|
-
value == "nil" ? nil : value
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
29
|
+
end # Base
|
66
30
|
end
|
data/lib/siphon/nil.rb
ADDED
data/lib/siphon/version.rb
CHANGED
data/lib/siphon.rb
CHANGED
@@ -2,9 +2,10 @@ require "bundler/setup"
|
|
2
2
|
|
3
3
|
require "active_support/core_ext"
|
4
4
|
|
5
|
-
|
6
5
|
require "siphon/version"
|
7
6
|
require "siphon/base"
|
7
|
+
require "siphon/adapter"
|
8
|
+
require "siphon/nil"
|
8
9
|
|
9
10
|
module Siphon
|
10
11
|
# Your code goes here...
|
@@ -13,14 +14,8 @@ module Siphon
|
|
13
14
|
@siphon = Siphon::Base.new(args)
|
14
15
|
end
|
15
16
|
|
16
|
-
def siphon_scopes
|
17
|
-
@siphon and Hash[ @siphon.scopes.map {|k, v| v||= "nil"; [k, v]} ]
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
17
|
end
|
22
18
|
|
23
19
|
ActiveSupport.on_load :action_controller do
|
24
20
|
include Siphon
|
25
|
-
helper_method :siphon_scopes
|
26
21
|
end
|
data/siphon.gemspec
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
class Formobj
|
2
|
+
|
3
|
+
include Virtus.model
|
4
|
+
|
5
|
+
attribute :name, String
|
6
|
+
attribute :after, Integer
|
7
|
+
attribute :admin, Boolean
|
8
|
+
attribute :order, String
|
9
|
+
attribute :stale, Siphon::Nil
|
10
|
+
|
11
|
+
attr_accessor :sinfull
|
12
|
+
|
13
|
+
extend ActiveModel::Naming
|
14
|
+
include ActiveModel::Conversion
|
15
|
+
include ActiveModel::Validations
|
16
|
+
|
17
|
+
|
18
|
+
# def initialize(params = {})
|
19
|
+
# params.each do |attr, value|
|
20
|
+
# self.public_send("#{attr}=", value)
|
21
|
+
# end if params
|
22
|
+
|
23
|
+
# super()
|
24
|
+
# end
|
25
|
+
|
26
|
+
def scopes
|
27
|
+
attributes
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
describe Siphon::Adapter, focus: true do
|
5
|
+
|
6
|
+
describe "scope ->() {...}" do
|
7
|
+
context "when Formobject#attributes is coerced to Siphon::Nil" do
|
8
|
+
it "will not return the scope if the argument is false" do
|
9
|
+
formobj = Formobj.new(stale: "0")
|
10
|
+
|
11
|
+
scopes_hash = Siphon::Adapter.new(formobj).call
|
12
|
+
expect(scopes_hash).to eq({})
|
13
|
+
end
|
14
|
+
|
15
|
+
it "will return the scope with nil if the argument's true" do
|
16
|
+
formobj = Formobj.new(stale: "1")
|
17
|
+
|
18
|
+
scopes_hash = Siphon::Adapter.new(formobj).call
|
19
|
+
expect(scopes_hash).to eq({stale: nil})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "scope ->(args) {...}" do
|
25
|
+
context "when Formobject takes string values" do
|
26
|
+
it "will return Formobject typecasted values" do
|
27
|
+
formobj = Formobj.new(after: "1950")
|
28
|
+
|
29
|
+
scopes_hash = Siphon::Adapter.new(formobj).call
|
30
|
+
expect(scopes_hash).to eq(after: 1950)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when Formobject#attributes returns empty strings" do
|
35
|
+
it "return hash withtout empty strings" do
|
36
|
+
formobj = Formobj.new(admin: "")
|
37
|
+
|
38
|
+
scopes_hash = Siphon::Adapter.new(formobj).call
|
39
|
+
expect(scopes_hash.keys).to_not include(:admin)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/siphon/base_spec.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
class Tree
|
4
|
-
end
|
5
3
|
|
6
4
|
describe Siphon::Base do
|
7
5
|
|
@@ -19,79 +17,55 @@ describe Siphon::Base do
|
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
|
-
describe "#
|
20
|
+
describe "#with", broken: true do
|
23
21
|
|
24
|
-
it "returns
|
22
|
+
it "returns proxy" do
|
25
23
|
siphon = Siphon::Base.new("")
|
26
24
|
|
27
|
-
expect(siphon.
|
25
|
+
expect(siphon.with({published: nil})).to be_kind_of(Siphon::Proxy)
|
28
26
|
end
|
29
27
|
|
30
|
-
it "sets
|
31
|
-
siphon = Siphon::Base.new("").
|
28
|
+
it "sets params" do
|
29
|
+
siphon = Siphon::Base.new("").with(published: :integer)
|
32
30
|
|
33
|
-
expect(siphon.
|
31
|
+
expect(siphon.params).to eq(published: :integer)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
|
-
describe "#
|
38
|
-
|
39
|
-
it "triggers a scope with string arg (default)" do
|
35
|
+
describe "#proxy", broken: true do
|
36
|
+
it "handles the scope calls" do
|
40
37
|
relation = double
|
41
|
-
siphon = Siphon::Base.new(relation)
|
42
|
-
|
43
|
-
expect(relation).to receive(:name).with("jack")
|
44
|
-
|
45
|
-
siphon.filter(name: "jack")
|
46
|
-
end
|
47
|
-
|
48
|
-
it "triggers a scope with an integer arg" do
|
49
|
-
relation = double
|
50
|
-
siphon = Siphon::Base.new(relation).has_scopes(age_gt: :integer)
|
51
|
-
|
52
|
-
expect(relation).to receive(:age_gt).with(18)
|
53
|
-
|
54
|
-
siphon.filter(age_gt: "18")
|
55
|
-
end
|
38
|
+
siphon = Siphon::Base.new(relation)
|
56
39
|
|
57
|
-
it "triggers a scope with a boolean arg" do
|
58
|
-
relation = double
|
59
|
-
siphon = Siphon::Base.new(relation).has_scopes(admin: :boolean)
|
60
40
|
|
61
|
-
expect(relation).to receive(:admin).with(
|
41
|
+
expect(relation).to receive(:admin).with("yes").and_return(relation)
|
42
|
+
expect(relation).to receive(:published).with("yes").and_return(relation)
|
62
43
|
|
63
|
-
siphon.
|
44
|
+
siphon.with(admin: "yes", published: "yes").admin("no").published
|
64
45
|
end
|
46
|
+
end
|
65
47
|
|
66
|
-
|
48
|
+
describe "#recall", broken: true do
|
49
|
+
it "exposes a proxy whose method_missing will record calls to be recalled conditionally" do
|
67
50
|
relation = double
|
68
|
-
|
51
|
+
expect(relation).to receive(:admin).with("yes").and_return(relation)
|
52
|
+
expect(relation).to receive(:published).with("yes").and_return(relation)
|
69
53
|
|
70
|
-
|
71
|
-
expect(relation).to receive(:age_gt).with(18).and_return(relation)
|
72
|
-
|
73
|
-
siphon.filter(age_gt: "18", admin: "false")
|
54
|
+
Siphon::Base.new(relation).recall.admin("no").published.when({admin: "yes", published: "yes"})
|
74
55
|
end
|
56
|
+
end
|
75
57
|
|
76
|
-
|
58
|
+
describe "functional testing", broken: true do
|
59
|
+
it "kinda works" do
|
77
60
|
relation = double
|
78
|
-
|
61
|
+
formobj = Formobj.new(admin: true, after: 1950, name: "")
|
79
62
|
|
80
|
-
expect(relation).to receive(:
|
81
|
-
expect(relation).
|
63
|
+
expect(relation).to receive(:admin).with(true).and_return(relation)
|
64
|
+
expect(relation).to receive(:after).with(1950).and_return(relation)
|
82
65
|
|
83
|
-
|
66
|
+
Siphon::Base.new(relation).admin(false).after(1).scope(formobj)
|
84
67
|
end
|
85
68
|
end
|
86
69
|
|
87
|
-
# TESTING PRIVATE METHOD !
|
88
|
-
describe "PRIVATE !! : #map_scope_datatypes" do
|
89
|
-
|
90
|
-
it "maps datatype date" do
|
91
|
-
pending "IGNORE & DELETE if not working"
|
92
|
-
siphon = Siphon::Base.new("")
|
93
|
-
siphon.map_scope_datatypes({age_gt: "18"}, {age_gt: :integer}).should == {age_gt: 18}
|
94
|
-
end
|
95
|
-
end
|
96
70
|
|
97
71
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
describe Formobj, idle: false do
|
5
|
+
describe "#attributes", idle: true do
|
6
|
+
it "returns a hash" do
|
7
|
+
formobj = Formobj.new(name: "proust", admin: "true", after: "1800")
|
8
|
+
|
9
|
+
expect(formobj.attributes).to be_a(Hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "includes attributes not passed in initializer" do
|
13
|
+
formobj = Formobj.new(name: "proust")
|
14
|
+
|
15
|
+
expect(formobj.attributes.keys).to include(:admin, :order, :after)
|
16
|
+
# expect(formobj.attributes).to eq("blza")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "does not include PORO attributes (attr_accessor)" do
|
20
|
+
formobj = Formobj.new(name: "proust")
|
21
|
+
|
22
|
+
expect(formobj.attributes.keys).to_not include(:sinfull)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when params.value's empty strings" do
|
26
|
+
it "returns empty string" do
|
27
|
+
formobj = Formobj.new(name: "", admin: "", after: "")
|
28
|
+
|
29
|
+
expect(formobj.name).to eq ""
|
30
|
+
expect(formobj.after).to eq ""
|
31
|
+
expect(formobj.admin).to eq ""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when params.value's nil" do
|
36
|
+
it "returns nil" do
|
37
|
+
formobj = Formobj.new(name: nil, admin: nil, after: nil)
|
38
|
+
|
39
|
+
expect(formobj.name).to eq nil
|
40
|
+
expect(formobj.after).to eq nil
|
41
|
+
expect(formobj.admin).to eq nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when param is not present" do
|
46
|
+
it "returns default values" do
|
47
|
+
formobj = Formobj.new()
|
48
|
+
|
49
|
+
expect(formobj.name).to eq "nabokov"
|
50
|
+
expect(formobj.after).to eq 1950
|
51
|
+
expect(formobj.admin).to eq false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when params.value has string values" do
|
56
|
+
it "returns typecasted values" do
|
57
|
+
formobj = Formobj.new(name: "proust", admin: "true", after: "1800")
|
58
|
+
|
59
|
+
expect(formobj.name).to eq "proust"
|
60
|
+
expect(formobj.after).to eq 1800
|
61
|
+
expect(formobj.admin).to eq true
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns true with '1' " do
|
65
|
+
formobj = Formobj.new(admin: "1")
|
66
|
+
expect(formobj.admin).to eq true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "DOES NOT RETURN TRUE with 'random' " do
|
70
|
+
formobj = Formobj.new(admin: "random")
|
71
|
+
expect(formobj.admin).to_not eq true
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns false with 'false' " do
|
75
|
+
formobj = Formobj.new(admin: "false")
|
76
|
+
expect(formobj.admin).to eq false
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns false with '0' " do
|
80
|
+
formobj = Formobj.new(admin: "0")
|
81
|
+
expect(formobj.admin).to eq false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#attributes with nil arg" do
|
87
|
+
it "will do something ???? " do
|
88
|
+
pending "Put this in the siphon::nil tests...."
|
89
|
+
formobj = Formobj.new(stale: "0")
|
90
|
+
|
91
|
+
expect(formobj.stale).to eq("")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
describe "Siphon::Mapper", broken: true do
|
5
|
+
|
6
|
+
|
7
|
+
describe "#yo" do
|
8
|
+
|
9
|
+
it "does awesome stuff" do
|
10
|
+
|
11
|
+
mapper = Siphon::Mapper.new([:active, name: "jfk", age: 18, published: true], [{name: "clinton"}, {age: "12"}, :published])
|
12
|
+
|
13
|
+
|
14
|
+
expect(mapper.types).to eq( "" )
|
15
|
+
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/siphon_spec.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
1
2
|
|
3
|
+
class Controller
|
4
|
+
include Siphon
|
5
|
+
end
|
2
6
|
|
3
7
|
describe Siphon do
|
8
|
+
let(:ctrl) {Controller.new}
|
4
9
|
|
5
10
|
describe "#new" do
|
6
|
-
|
7
11
|
it "shoot a Siphon::Base instance" do
|
8
|
-
expect(
|
12
|
+
expect(ctrl.siphon("")).to be_an_instance_of(Siphon::Base)
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,18 +4,20 @@
|
|
4
4
|
# loaded once.
|
5
5
|
#
|
6
6
|
require "siphon"
|
7
|
+
require "pry"
|
7
8
|
|
9
|
+
require "virtus"
|
10
|
+
require "active_support"
|
11
|
+
require "active_model"
|
12
|
+
require "class/formobj"
|
8
13
|
|
9
|
-
class ActiveRelation
|
10
|
-
def self.title_with
|
11
|
-
end
|
12
|
-
end
|
13
14
|
|
14
15
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
15
16
|
RSpec.configure do |config|
|
16
17
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
17
18
|
config.run_all_when_everything_filtered = true
|
18
|
-
config.
|
19
|
+
config.filter_run_excluding( broken: true, idle: true )
|
20
|
+
# config.filter_run :focus
|
19
21
|
|
20
22
|
# Run specs in random order to surface order dependencies. If you find an
|
21
23
|
# order dependency and want to debug it, you can fix the order by providing
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: siphon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
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-
|
11
|
+
date: 2013-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activemodel
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: virtus
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
description: Siphon enables you to easily apply/combine/exclude your ActiveRecord
|
84
112
|
scopes with params
|
85
113
|
email:
|
@@ -96,12 +124,19 @@ files:
|
|
96
124
|
- LICENSE.txt
|
97
125
|
- README.md
|
98
126
|
- Rakefile
|
127
|
+
- TODO.md
|
99
128
|
- lib/siphon.rb
|
129
|
+
- lib/siphon/adapter.rb
|
100
130
|
- lib/siphon/base.rb
|
131
|
+
- lib/siphon/nil.rb
|
101
132
|
- lib/siphon/version.rb
|
102
133
|
- siphon.gemspec
|
103
134
|
- spec/class/active_relation.rb
|
135
|
+
- spec/class/formobj.rb
|
136
|
+
- spec/siphon/adapter_spec.rb
|
104
137
|
- spec/siphon/base_spec.rb
|
138
|
+
- spec/siphon/formobj_spec.rb
|
139
|
+
- spec/siphon/mapper_spec.rb
|
105
140
|
- spec/siphon_spec.rb
|
106
141
|
- spec/spec_helper.rb
|
107
142
|
homepage: http://github.com/charly/siphon
|
@@ -131,6 +166,10 @@ summary: Siphon enables you to easily apply/combine/exclude your ActiveRecord sc
|
|
131
166
|
with params
|
132
167
|
test_files:
|
133
168
|
- spec/class/active_relation.rb
|
169
|
+
- spec/class/formobj.rb
|
170
|
+
- spec/siphon/adapter_spec.rb
|
134
171
|
- spec/siphon/base_spec.rb
|
172
|
+
- spec/siphon/formobj_spec.rb
|
173
|
+
- spec/siphon/mapper_spec.rb
|
135
174
|
- spec/siphon_spec.rb
|
136
175
|
- spec/spec_helper.rb
|