chainable_methods 0.2.0 → 0.2.1
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/.travis.yml +13 -2
- data/README.md +27 -14
- data/chainable_methods.gemspec +1 -0
- data/fixtures/vcr_cassettes/github-test.yml +3 -3
- data/lib/chainable_methods.rb +12 -0
- data/lib/chainable_methods/version.rb +1 -1
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 620ba46af2bafae375678b698d4cc880f44c24b8
|
4
|
+
data.tar.gz: 9525185333b1e4cb6e2a9481758c7e4323984173
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8ddb94ce68e740265f41cc988ba93a6bdaf73fe09b2049bb57092ecf77610762393f040f76722df3d7d8d90911e65170bbe660cb3fc2076ded534a78c88df19
|
7
|
+
data.tar.gz: 868f95a4fb3322b3977b412679e79ce8cdb537b91217926da4d1d11b492027853145068b9b74775d0f04ecc6b3506c3603c6ca8e58b6e7a0605fe741cfa39b96
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
2
3
|
rvm:
|
3
|
-
- 2.3.
|
4
|
-
before_install:
|
4
|
+
- 2.3.1
|
5
|
+
before_install:
|
6
|
+
- gem install bundler -v 1.11.2
|
7
|
+
script:
|
8
|
+
- xvfb-run bundle exec rake test
|
9
|
+
notifications:
|
10
|
+
email:
|
11
|
+
recipients:
|
12
|
+
- boss@akitaonrails.com
|
13
|
+
addons:
|
14
|
+
code_climate:
|
15
|
+
repo_token: 9336cb6031790cc0f5501db2d74c8671c6ff1b74eb2883c0456a5722a775d655
|
data/README.md
CHANGED
@@ -1,20 +1,30 @@
|
|
1
1
|
# Chainable Methods
|
2
2
|
|
3
|
+
<a href="https://codeclimate.com/repos/57659a6019dc0b459200205b/feed"><img src="https://codeclimate.com/repos/57659a6019dc0b459200205b/badges/fbf8f254fa716b481c40/gpa.svg" /></a>
|
4
|
+
|
5
|
+
<a href="https://travis-ci.org/akitaonrails/chainable_methods"><img src="https://travis-ci.org/akitaonrails/chainable_methods.svg?branch=master" /></a>
|
6
|
+
|
7
|
+
<a href="https://codeclimate.com/repos/57659a6019dc0b459200205b/coverage"><img src="https://codeclimate.com/repos/57659a6019dc0b459200205b/badges/fbf8f254fa716b481c40/coverage.svg" /></a>
|
8
|
+
|
9
|
+
<a href="https://codeclimate.com/repos/57659a6019dc0b459200205b/feed"><img src="https://codeclimate.com/repos/57659a6019dc0b459200205b/badges/fbf8f254fa716b481c40/issue_count.svg" /></a>
|
10
|
+
|
3
11
|
The Elixir language is doing great and within its many incredible features is the famous "Pipe Operator". Other popular functional languages like Haskell and F# sport a similar feature.
|
4
12
|
|
5
13
|
It allows you to do constructs such as this:
|
6
14
|
|
7
|
-
```
|
15
|
+
```elixir
|
16
|
+
require Integer
|
8
17
|
1..100_000
|
9
18
|
|> Stream.map(&(&1 * 3))
|
10
|
-
|> Stream.filter(
|
19
|
+
|> Stream.filter(&(Integer.is_odd(&1)))
|
11
20
|
|> Enum.sum
|
12
21
|
```
|
13
22
|
|
14
23
|
In a nutshell, this is taking the previous returning value and automatically passing it as the first argument of the following function call, so it's sort of equivalent to do this:
|
15
24
|
|
16
|
-
```
|
17
|
-
|
25
|
+
```elixir
|
26
|
+
require Integer
|
27
|
+
Enum.sum(Enum.filter(Enum.map(1..100_000, &(&1 * 3)), &(Integer.is_odd(&1))))
|
18
28
|
```
|
19
29
|
|
20
30
|
(In F# it's even more important to make proper left-to-right type inference.)
|
@@ -23,13 +33,13 @@ This is how we would usually do it, but with the Pipe Operator it becomes incred
|
|
23
33
|
|
24
34
|
Now, in the Ruby world, we would prefer to do it in a more Object Oriented fashion, with chained methods like this:
|
25
35
|
|
26
|
-
```
|
36
|
+
```ruby
|
27
37
|
object.method_1.method_2(argument).method_3 { |x| do_something(x) }.method_4
|
28
38
|
```
|
29
39
|
|
30
40
|
This is how we do things in Rails, for example, Arel coming into mind:
|
31
41
|
|
32
|
-
```
|
42
|
+
```ruby
|
33
43
|
User.first.comments.where(created_at: 2.days.ago..Time.current).limit(5)
|
34
44
|
```
|
35
45
|
|
@@ -57,7 +67,7 @@ Or install it yourself as:
|
|
57
67
|
|
58
68
|
## Usage
|
59
69
|
|
60
|
-
```
|
70
|
+
```ruby
|
61
71
|
# create your Module with composable 'functions'
|
62
72
|
module MyModule
|
63
73
|
include ChainableMethods
|
@@ -79,7 +89,7 @@ end
|
|
79
89
|
|
80
90
|
And now we can build something like this:
|
81
91
|
|
82
|
-
```
|
92
|
+
```ruby
|
83
93
|
MyModule.
|
84
94
|
chain_from(some_text).
|
85
95
|
upcase. # this calls a method from the string in 'some_text'
|
@@ -91,7 +101,7 @@ MyModule.
|
|
91
101
|
|
92
102
|
And that's it. This would be the equivalent of doing something more verbose like this:
|
93
103
|
|
94
|
-
```
|
104
|
+
```ruby
|
95
105
|
a = some_text.upcase
|
96
106
|
b = MyModule.method_a(a)
|
97
107
|
c = MyModule.method_b(b, "something")
|
@@ -102,7 +112,7 @@ The recommend approach is to create modules to serve as "namespaces" for collect
|
|
102
112
|
|
103
113
|
Sometimes we have adhoc transformations. We usually have to storage intermediate states as dangling variables like this:
|
104
114
|
|
105
|
-
```
|
115
|
+
```ruby
|
106
116
|
text = "hello http:///www.google.com world"
|
107
117
|
url = URI.extract(text).first }
|
108
118
|
uri = URI.parse(url)
|
@@ -112,10 +122,10 @@ title = Nokogiri::HTML(body).css("h1").first.text.strip
|
|
112
122
|
|
113
123
|
Or now, we can just chain them together like this:
|
114
124
|
|
115
|
-
```
|
125
|
+
```ruby
|
116
126
|
CM("hello http:///www.google.com world")
|
117
|
-
.
|
118
|
-
.
|
127
|
+
.URI.extract.first
|
128
|
+
.URI.parse
|
119
129
|
.chain { |uri| open(uri).read }
|
120
130
|
.chain { |body| Nokogiri::HTML(body).css("h1") }
|
121
131
|
.first.text.strip
|
@@ -150,7 +160,7 @@ v0.1.1
|
|
150
160
|
v0.1.2
|
151
161
|
- introduces a shortcut global method 'CM' to be used like this:
|
152
162
|
|
153
|
-
```
|
163
|
+
```ruby
|
154
164
|
CM(2, ['a', 'b', 'c'])
|
155
165
|
.[]
|
156
166
|
.upcase
|
@@ -164,6 +174,9 @@ v0.1.3
|
|
164
174
|
v0.1.4
|
165
175
|
- makes the ChainableMethods module "includable" and it automatically makes all instance methods of the parent Module as class methods that can be easily chainable without having to declare all of them as `def self.method` first. So you can do it like this:
|
166
176
|
|
177
|
+
v0.2.1
|
178
|
+
- use a const_get trick to allow to chain Module or Class names directly in the dot notation. Inspired by [this gist](https://gist.github.com/bkerley/754df43c98e116e82003). Kudos to @bkerley for the idea and @BonzoESC for bringing it out.
|
179
|
+
|
167
180
|
## License
|
168
181
|
|
169
182
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/chainable_methods.gemspec
CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'nokogiri', '~> 1.6', '>= 1.6.8'
|
27
27
|
spec.add_development_dependency 'vcr', '~> 3.0', '>= 3.0.3'
|
28
28
|
spec.add_development_dependency 'webmock', '~> 2.1', '>= 2.1.0'
|
29
|
+
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.5', '>= 0.5.2'
|
29
30
|
end
|
@@ -27,7 +27,7 @@ http_interactions:
|
|
27
27
|
body:
|
28
28
|
encoding: UTF-8
|
29
29
|
string: ''
|
30
|
-
http_version:
|
30
|
+
http_version:
|
31
31
|
recorded_at: Sat, 18 Jun 2016 18:45:29 GMT
|
32
32
|
- request:
|
33
33
|
method: get
|
@@ -65,7 +65,7 @@ http_interactions:
|
|
65
65
|
X-Ua-Compatible:
|
66
66
|
- IE=Edge,chrome=1
|
67
67
|
Set-Cookie:
|
68
|
-
- _gh_sess=
|
68
|
+
- _gh_sess=eyJzZXNzaT4ifX19--861decdf56396532;
|
69
69
|
path=/; secure; HttpOnly
|
70
70
|
- _octo=GH1.1.49047256.1466275531; domain=.github.com; path=/; expires=Mon,
|
71
71
|
18 Jun 2018 18:45:31 -0000
|
@@ -1528,6 +1528,6 @@ http_interactions:
|
|
1528
1528
|
NDgtMS40OEw2IDYuNTJsMy43NS0zLjc1IDEuNDggMS40OHoiPjwvcGF0aD48
|
1529
1529
|
L3N2Zz4KICAgIDwvYnV0dG9uPgogIDwvZGl2Pgo8L2Rpdj4KCiAgPC9ib2R5
|
1530
1530
|
Pgo8L2h0bWw+Cgo=
|
1531
|
-
http_version:
|
1531
|
+
http_version:
|
1532
1532
|
recorded_at: Sat, 18 Jun 2016 18:45:30 GMT
|
1533
1533
|
recorded_with: VCR 3.0.3
|
data/lib/chainable_methods.rb
CHANGED
@@ -35,6 +35,10 @@ module ChainableMethods
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def method_missing(method_name, *args, &block)
|
38
|
+
if is_constant?(method_name)
|
39
|
+
return ChainableMethods::Link.new( @state, ::Kernel.const_get(method_name) )
|
40
|
+
end
|
41
|
+
|
38
42
|
local_response = @state.respond_to?(method_name)
|
39
43
|
context_response = @context.respond_to?(method_name)
|
40
44
|
|
@@ -52,5 +56,13 @@ module ChainableMethods
|
|
52
56
|
def unwrap
|
53
57
|
@state
|
54
58
|
end
|
59
|
+
|
60
|
+
private def is_constant?(method_name)
|
61
|
+
method_name_start = method_name.to_s.chars.first
|
62
|
+
if method_name_start =~ /\A[[:alpha:]]+\z/i
|
63
|
+
return ( method_name_start.upcase == method_name_start )
|
64
|
+
end
|
65
|
+
false
|
66
|
+
end
|
55
67
|
end
|
56
68
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chainable_methods
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AkitaOnRails
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -126,6 +126,26 @@ dependencies:
|
|
126
126
|
- - ">="
|
127
127
|
- !ruby/object:Gem::Version
|
128
128
|
version: 2.1.0
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: codeclimate-test-reporter
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - "~>"
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0.5'
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.5.2
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.5'
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 0.5.2
|
129
149
|
description: The idea is to allow for a more functional way of organizing code within
|
130
150
|
a module and being able to chain those methdos together, where the result of the
|
131
151
|
first method serves as the first argument of the next method in the chain.
|
@@ -168,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
188
|
version: '0'
|
169
189
|
requirements: []
|
170
190
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
191
|
+
rubygems_version: 2.6.4
|
172
192
|
signing_key:
|
173
193
|
specification_version: 4
|
174
194
|
summary: Just a simple experiment to allow for a behavior similar to [Elixir|Haskell|F#]'s
|