liquid_stream 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +1 -0
- data/lib/liquid_stream/errors.rb +4 -0
- data/lib/liquid_stream/stream.rb +116 -0
- data/lib/liquid_stream/streams.rb +37 -0
- data/lib/liquid_stream/version.rb +3 -0
- data/lib/liquid_stream.rb +11 -0
- data/liquid_stream.gemspec +28 -0
- data/spec/fixtures/base.rb +28 -0
- data/spec/fixtures/blog.rb +5 -0
- data/spec/fixtures/blog_stream.rb +10 -0
- data/spec/fixtures/blogs_stream.rb +5 -0
- data/spec/fixtures/comment.rb +3 -0
- data/spec/fixtures/image.rb +3 -0
- data/spec/fixtures/image_stream.rb +14 -0
- data/spec/fixtures/images_stream.rb +7 -0
- data/spec/fixtures/post.rb +5 -0
- data/spec/fixtures/post_stream.rb +5 -0
- data/spec/fixtures/posts_stream.rb +5 -0
- data/spec/integrations/allow_chaining_via_stream_spec.rb +59 -0
- data/spec/liquid_stream/stream_spec.rb +127 -0
- data/spec/liquid_stream/streams_spec.rb +85 -0
- data/spec/liquid_stream/utils.rb +24 -0
- data/spec/liquid_stream/utils_spec.rb +43 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/streamer_resetter.rb +15 -0
- metadata +190 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea6db0105ace4ed3e0e6b0a33985dd864bf61301
|
4
|
+
data.tar.gz: 99c6cde0700fd17641889917f0ab31b73bb9429e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 568824a23e3a5cee379d2f902b65021e95236be679c48f070399cc8414bb84358692e9540fa196725bc87c4ebe4d717c00a29f4293d4f9970d921601d6e5bb3a
|
7
|
+
data.tar.gz: 856c0fe62d1664c3c750cdbe434a9396219c61e4290898dd714cee2fa83e631c903463dff7339787b314db0d5af6bd70c767c5fbc28d703cb415c4ead52d8d29
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ramon Tayag
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# LiquidStream
|
2
|
+
|
3
|
+
Allows chaining of Liquid objects with a clean DSL, which allows a more Ruby-like way to traverse Liquid objects. For example:
|
4
|
+
|
5
|
+
{% for post in posts.published.popular do %}
|
6
|
+
{{ post.name }}
|
7
|
+
{% endfor %}
|
8
|
+
|
9
|
+
[See why](https://github.com/Shopify/liquid/issues/29).
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'liquid_stream'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install liquid_stream
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Create a stream that represents one post:
|
28
|
+
|
29
|
+
class PostStream < LiquidStream::Stream
|
30
|
+
stream :name
|
31
|
+
end
|
32
|
+
|
33
|
+
Create a stream collection that represents a collection of posts:
|
34
|
+
|
35
|
+
class PostsStream < LiquidStream::Streams
|
36
|
+
stream :published
|
37
|
+
stream :popular
|
38
|
+
end
|
39
|
+
|
40
|
+
posts = user.posts
|
41
|
+
posts_stream = PostsStream.new(posts)
|
42
|
+
|
43
|
+
Liquid::Template.parse("liquid here").render('posts' => posts_stream)
|
44
|
+
|
45
|
+
### "Accepting" Arguments
|
46
|
+
|
47
|
+
Given:
|
48
|
+
|
49
|
+
class ImageStream < LiquidStream::Stream
|
50
|
+
stream :process, as: :image do |command|
|
51
|
+
source.process command
|
52
|
+
end
|
53
|
+
|
54
|
+
stream :url
|
55
|
+
end
|
56
|
+
|
57
|
+
image = Image.find(2323)
|
58
|
+
image_stream = ImageStream.new(image)
|
59
|
+
|
60
|
+
Then, in Liquid:
|
61
|
+
|
62
|
+
{{image_stream.process["200x200"].process["grayscale"].url | image_tag}}
|
63
|
+
|
64
|
+
### Stream Context
|
65
|
+
|
66
|
+
LiquidStream has a notion of context too. To avoid confusion with Liquid's context, let's call it stream_context. The reason there's a stream context is so that if you have a need to share information within a chained stream, then you need to pass it as a hash. I found this useful when the I needed the streams to know about which controller it was being used:
|
67
|
+
|
68
|
+
class PostsController < ApplicationController
|
69
|
+
def show
|
70
|
+
post = Post.find(params[:id])
|
71
|
+
post_stream = PostStream.new(post, controller: self)
|
72
|
+
Liquid::Template.parse("{{post.blog.children.first.url}}").render('posts' => post_stream)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class PostStream < LiquidStream::Stream
|
77
|
+
include Rails.application.helpers.url_helpers
|
78
|
+
|
79
|
+
def url
|
80
|
+
if controller.request.fullpath =~ /^preview/
|
81
|
+
polymorphic_path :preview, source
|
82
|
+
else
|
83
|
+
polyorphic_path source
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def controller
|
90
|
+
stream_context[:controller]
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
It's a very specific use-case, but this allows you to render the links to other posts a different way if the person viewing is viewing the post from "/preview/posts/:id" compared to what is rendered when in "/posts/:id". If you find other uses please fork this repo and add it to this readme.
|
96
|
+
|
97
|
+
## Stream is a Liquid::Drop
|
98
|
+
|
99
|
+
A stream is a drop - with extra stuff added on to it.
|
100
|
+
|
101
|
+
## Stream carefully
|
102
|
+
|
103
|
+
You may not want to expose something that will make it easy for a user to break your system. For example, let's say you have 2 million posts, then you won't want to expose all of those posts through a stream:
|
104
|
+
|
105
|
+
posts = Post.scoped # ActiveRecord's scoped returns a lazily executed Arel. If you call #all on this, you'll get 2 million records
|
106
|
+
posts_stream = PostsStream.new(posts)
|
107
|
+
posts_stream.to_a # boom!
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
1. Fork it
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
115
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module LiquidStream
|
2
|
+
class Stream < Liquid::Drop
|
3
|
+
|
4
|
+
attr_reader :source, :stream_context
|
5
|
+
|
6
|
+
def initialize(source=nil, stream_context={})
|
7
|
+
@source = source
|
8
|
+
@stream_context = stream_context
|
9
|
+
end
|
10
|
+
|
11
|
+
class_attribute :liquid_streams
|
12
|
+
|
13
|
+
def self.stream(method_name, options={}, &block)
|
14
|
+
self.liquid_streams ||= {}
|
15
|
+
self.liquid_streams[method_name] = {options: options, block: block}
|
16
|
+
|
17
|
+
# DefinesStreamMethod
|
18
|
+
if block_given?
|
19
|
+
if options.has_key?(:matching)
|
20
|
+
self.send :define_method, method_name do |method_arg|
|
21
|
+
instance_exec(method_arg, &block)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
self.send :define_method, method_name do |*method_args|
|
25
|
+
class_name = generate_stream_class_name method_name
|
26
|
+
klass = find_or_create_stream_class class_name
|
27
|
+
stream = klass.new(source, stream_context)
|
28
|
+
klass.send :define_method, :before_method do |before_method_arg|
|
29
|
+
stream.instance_exec(before_method_arg, &block)
|
30
|
+
end
|
31
|
+
stream
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
self.send(:define_method, method_name) do |*args|
|
36
|
+
method_result = source.send(method_name)
|
37
|
+
options = self.class.liquid_streams[method_name][:options]
|
38
|
+
|
39
|
+
if method_result.respond_to?(:each)
|
40
|
+
# BuildsStreamClassName
|
41
|
+
streams_class_name = Utils.
|
42
|
+
streams_class_name_from(options[:as] || method_name)
|
43
|
+
|
44
|
+
# FailsIfStreamNotDefined
|
45
|
+
unless Object.const_defined?(streams_class_name)
|
46
|
+
fail StreamNotDefined, "`#{streams_class_name}` is not defined"
|
47
|
+
end
|
48
|
+
|
49
|
+
# BuildsStreamClass
|
50
|
+
streams_class = streams_class_name.constantize
|
51
|
+
|
52
|
+
# CreatesStreams
|
53
|
+
streams_class.new(method_result, stream_context)
|
54
|
+
else
|
55
|
+
stream_class_name = Utils.
|
56
|
+
stream_class_name_from(options[:as] || method_name)
|
57
|
+
|
58
|
+
if Object.const_defined?(stream_class_name)
|
59
|
+
stream_class = stream_class_name.constantize
|
60
|
+
stream_class.new(method_result, stream_context)
|
61
|
+
else
|
62
|
+
method_result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def before_method(method_name)
|
70
|
+
stream_name = matching_stream_names_for(method_name).first
|
71
|
+
if stream_name
|
72
|
+
options = self.liquid_streams[stream_name][:options]
|
73
|
+
result = send(stream_name, method_name)
|
74
|
+
|
75
|
+
if options[:as]
|
76
|
+
if result.respond_to?(:each)
|
77
|
+
# TODO: implement this IF we need to
|
78
|
+
# streams_class_name = Util.stream_class_name_from(options[:as])
|
79
|
+
# streams_class = streams_class_name.constantize
|
80
|
+
else
|
81
|
+
stream_class_name = Utils.stream_class_name_from(options[:as])
|
82
|
+
stream_class = stream_class_name.constantize
|
83
|
+
stream_class.new(result, stream_context)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def matching_stream_names_for(method_name)
|
94
|
+
self.class.liquid_streams.select do |stream_name, data|
|
95
|
+
match_regex = data[:options][:matching]
|
96
|
+
(match_regex && method_name =~ match_regex) || match_regex.nil?
|
97
|
+
end.keys
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_or_create_stream_class class_name
|
101
|
+
klass = if Object.const_defined?(class_name)
|
102
|
+
class_name.constantize
|
103
|
+
else
|
104
|
+
Object.const_set(class_name, Class.new(LiquidStream::Stream))
|
105
|
+
end
|
106
|
+
klass
|
107
|
+
end
|
108
|
+
|
109
|
+
def generate_stream_class_name method_name
|
110
|
+
new_class_name = self.class.to_s.
|
111
|
+
gsub("Stream", "#{method_name.to_s.classify}Stream").
|
112
|
+
strip
|
113
|
+
new_class_name
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LiquidStream
|
2
|
+
class Streams < Stream
|
3
|
+
|
4
|
+
delegate :count, :size, to: :source
|
5
|
+
|
6
|
+
def initialize(source, stream_context={})
|
7
|
+
@source = source
|
8
|
+
@stream_context = stream_context
|
9
|
+
end
|
10
|
+
|
11
|
+
def first
|
12
|
+
@first ||= singleton_class.new(@source.first)
|
13
|
+
end
|
14
|
+
|
15
|
+
def last
|
16
|
+
@last ||= singleton_class.new(@source.last)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_a
|
20
|
+
@source.map do |object|
|
21
|
+
singleton_class.new(object)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
to_a.each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def singleton_class
|
32
|
+
@singleton_class ||= Utils.
|
33
|
+
stream_class_from(@stream_context[:method] || self.class)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'liquid_stream/version'
|
2
|
+
require 'active_support/core_ext/module/delegation'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'liquid'
|
5
|
+
require 'liquid_stream/stream'
|
6
|
+
require 'liquid_stream/streams'
|
7
|
+
require 'liquid_stream/errors'
|
8
|
+
require 'liquid_stream/utils'
|
9
|
+
|
10
|
+
module LiquidStream
|
11
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'liquid_stream/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "liquid_stream"
|
8
|
+
spec.version = LiquidStream::VERSION
|
9
|
+
spec.authors = ["Ramon Tayag"]
|
10
|
+
spec.email = ["ramon.tayag@gmail.com"]
|
11
|
+
spec.description = %q{Allows chaining of context aware Liquid drops}
|
12
|
+
spec.summary = %q{Allow a more Ruby-like interface to Liquid drops with context awareness}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec", '~> 2.13'
|
24
|
+
spec.add_development_dependency "activemodel", '~> 3.2'
|
25
|
+
spec.add_development_dependency "capybara", '~> 2.1'
|
26
|
+
spec.add_dependency 'liquid', '~> 2.2'
|
27
|
+
spec.add_dependency 'activesupport', '>= 3.0.0'
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Base
|
2
|
+
|
3
|
+
include ActiveModel::AttributeMethods
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
def initialize(attributes={})
|
7
|
+
@attributes = attributes.stringify_keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
@attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# can't seem to get the attributes to work properly... hack it
|
17
|
+
def method_missing(method_name, *args, &block)
|
18
|
+
if attributes.has_key?(method_name.to_s)
|
19
|
+
self.class.send(:define_method, method_name, *args) do
|
20
|
+
attributes[method_name.to_s]
|
21
|
+
end
|
22
|
+
send(method_name, *args)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class ImageStream < LiquidStream::Stream
|
2
|
+
|
3
|
+
# http://rubular.com/r/c8JxqJDytH
|
4
|
+
# allows stream["230x330"]
|
5
|
+
stream :image_resize, matching: /^\d+x\d+#?$/ do |size|
|
6
|
+
source.resize(size)
|
7
|
+
end
|
8
|
+
|
9
|
+
# allows stream.colorize["red"]
|
10
|
+
stream :colorize do |arg|
|
11
|
+
source.colorize arg
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LiquidStream do
|
4
|
+
|
5
|
+
it 'should allow chaining via the stream method' do
|
6
|
+
post_1 = Post.new(title: 'First')
|
7
|
+
post_2 = Post.new(title: 'Second')
|
8
|
+
post_3 = Post.new(title: 'Third')
|
9
|
+
|
10
|
+
blog = Blog.new(title: 'First blog')
|
11
|
+
posts = [post_1, post_2, post_3]
|
12
|
+
blog.stub(:posts) { posts }
|
13
|
+
popular_posts = [post_1, post_2]
|
14
|
+
posts.stub(:popular) { popular_posts }
|
15
|
+
|
16
|
+
blog_stream = BlogStream.new(blog)
|
17
|
+
|
18
|
+
template = <<-WUT
|
19
|
+
{{blog.posts.popular.size}}
|
20
|
+
<ul>
|
21
|
+
{% for post in blog.posts.popular %}
|
22
|
+
<li>{{post.title}}</li>
|
23
|
+
{% endfor %}
|
24
|
+
</ul>
|
25
|
+
WUT
|
26
|
+
html = Liquid::Template.parse(template).render('blog' => blog_stream)
|
27
|
+
doc = Capybara.string(html)
|
28
|
+
li_els = doc.all('ul li')
|
29
|
+
expect(li_els.size).to eq(2)
|
30
|
+
expect(li_els.first.text).to eq('First')
|
31
|
+
expect(li_els.last.text).to eq('Second')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should mimic accepting of arguments' do
|
35
|
+
template = <<-WUT
|
36
|
+
{{image["240x330#"]}}
|
37
|
+
{{image["250x350"]}}
|
38
|
+
{{image.colorize["yellow"]}}
|
39
|
+
{{blog.format["text"]}}
|
40
|
+
WUT
|
41
|
+
|
42
|
+
image = double
|
43
|
+
image.stub(:resize).with("240x330#") { "http://image.com/240x330.jpg" }
|
44
|
+
image.stub(:resize).with("250x350") { "http://image.com/250x350.jpg" }
|
45
|
+
image.stub(:colorize).with("yellow") { "http://image.com/yellow_colorized.jpg" }
|
46
|
+
blog = double
|
47
|
+
blog.stub(:format).with("text") { "some text" }
|
48
|
+
|
49
|
+
image_stream = ImageStream.new(image)
|
50
|
+
blog_stream = BlogStream.new(blog)
|
51
|
+
result = Liquid::Template.parse(template).
|
52
|
+
render('image' => image_stream, 'blog' => blog_stream)
|
53
|
+
expect(result).to include("http://image.com/240x330.jpg")
|
54
|
+
expect(result).to include("http://image.com/250x350.jpg")
|
55
|
+
expect(result).to include("http://image.com/yellow_colorized.jpg")
|
56
|
+
expect(result).to include("some text")
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LiquidStream::Stream do
|
4
|
+
|
5
|
+
describe '.stream' do
|
6
|
+
context 'streams to an enumerable' do
|
7
|
+
it 'allows access to another stream, using stream name as default reader' do
|
8
|
+
post_1 = Post.new(title: 'Post 1')
|
9
|
+
post_2 = Post.new(title: 'Post 2')
|
10
|
+
blog = Blog.new(title: 'Blog', posts: [post_1, post_2])
|
11
|
+
blog_stream = BlogStream.new(blog)
|
12
|
+
posts_stream = blog_stream.posts
|
13
|
+
expect(posts_stream).to be_kind_of(LiquidStream::Streams)
|
14
|
+
expect(posts_stream.size).to eq(2)
|
15
|
+
expect(posts_stream.first).to be_kind_of(PostStream)
|
16
|
+
expect(posts_stream.first.title).to eq('Post 1')
|
17
|
+
expect(posts_stream.last).to be_kind_of(PostStream)
|
18
|
+
expect(posts_stream.last.title).to eq('Post 2')
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'no stream class for the enumerable exists' do
|
22
|
+
it 'should raise LiquidStream::StreamNotDefined error' do
|
23
|
+
PostStream.stream(:comments)
|
24
|
+
post = Post.new(comments: [])
|
25
|
+
post_stream = PostStream.new(post)
|
26
|
+
expect {post_stream.comments}.
|
27
|
+
to raise_error(LiquidStream::StreamNotDefined,
|
28
|
+
"`CommentsStream` is not defined")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should make the class respond to the method' do
|
33
|
+
PostStream.stream(:comments)
|
34
|
+
post = Post.new(comments: [])
|
35
|
+
post_stream = PostStream.new(post)
|
36
|
+
expect(post_stream).to respond_to(:comments)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should have the stream name listed as an instance method' do
|
40
|
+
PostStream.stream(:comments)
|
41
|
+
PostStream.instance_methods.should include(:comments)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should pass in any context' do
|
45
|
+
post = Post.new(title: 'Post')
|
46
|
+
blog = Blog.new(title: 'Blog', posts: [post])
|
47
|
+
controller = double
|
48
|
+
blog_stream = BlogStream.new(blog, controller: controller)
|
49
|
+
posts_stream = blog_stream.posts
|
50
|
+
expect(posts_stream.stream_context).to include(controller: controller)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'streams to a non enumerable' do
|
55
|
+
context 'a stream class exists for the object' do
|
56
|
+
it 'should return the object instantiated in the stream' do
|
57
|
+
blog = Blog.new(title: 'Blog')
|
58
|
+
post = Post.new(title: 'Post', blog: blog)
|
59
|
+
PostStream.stream(:blog)
|
60
|
+
stream = PostStream.new(post)
|
61
|
+
expect(stream.blog).to be_kind_of(BlogStream)
|
62
|
+
expect(stream.blog.title).to eq('Blog')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should pass in any context' do
|
67
|
+
blog = Blog.new(title: 'Blog')
|
68
|
+
post = Post.new(title: 'Post', blog: blog)
|
69
|
+
controller = double
|
70
|
+
PostStream.stream(:blog)
|
71
|
+
stream = PostStream.new(post, controller: controller)
|
72
|
+
expect(stream.blog.stream_context).to eq(controller: controller)
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'given a specific stream class to use' do
|
76
|
+
it 'should instantiate the object in the stream class' do
|
77
|
+
blog = Blog.new(title: 'Blog')
|
78
|
+
post = Post.new(title: 'Post', blog: blog)
|
79
|
+
PostStream.stream(:blog, as: 'PostStream')
|
80
|
+
stream = PostStream.new(post)
|
81
|
+
expect(stream.blog).to be_kind_of(PostStream)
|
82
|
+
expect(stream.blog.title).to eq('Blog')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'no stream class exists for the object' do
|
87
|
+
it 'returns that object' do
|
88
|
+
post = Post.new(title: 'PostMan')
|
89
|
+
post_stream = PostStream.new(post)
|
90
|
+
expect(post_stream.title).to eq('PostMan')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'given the stream class name' do
|
96
|
+
it 'should use the given stream class' do
|
97
|
+
comment = Comment.new(body: 'Hi')
|
98
|
+
post = Post.new(title: 'Post', comments: [comment])
|
99
|
+
PostStream.stream(:comments, as: 'BlogsStream')
|
100
|
+
post_stream = PostStream.new(post)
|
101
|
+
expect(post_stream.comments.first).to be_kind_of(BlogStream)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'through option is given' do
|
106
|
+
context 'matching option is given' do
|
107
|
+
it 'should execute the matching liquid stream' do
|
108
|
+
image = double
|
109
|
+
image.stub(:resize).with('2x2') { 'http://image.com/2x2.jpg'}
|
110
|
+
stream = ImageStream.new(image)
|
111
|
+
expect(stream['2x2']).to eq('http://image.com/2x2.jpg')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context '`prefix` is set to true' do
|
116
|
+
it 'should delegate work, passing any args to the given "through" method' do
|
117
|
+
image = double
|
118
|
+
image.stub(:colorize).with('red') { 'F00' }
|
119
|
+
|
120
|
+
stream = ImageStream.new(image)
|
121
|
+
expect(stream.colorize['red']).to eq('F00')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LiquidStream::Streams do
|
4
|
+
|
5
|
+
describe '#first' do
|
6
|
+
context 'given the stream method data' do
|
7
|
+
it 'should return an stream derived from the given stream method' do
|
8
|
+
comments = ['a', 'b']
|
9
|
+
streams = described_class.new(comments, method: :posts)
|
10
|
+
post_stream = double
|
11
|
+
PostStream.stub(:new).with('a') { post_stream }
|
12
|
+
expect(streams.first).to eq(post_stream)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#last' do
|
18
|
+
context 'given the stream method data' do
|
19
|
+
it 'should return an stream derived from the given stream method' do
|
20
|
+
comments = ['a', 'b']
|
21
|
+
streams = described_class.new(comments, method: :posts)
|
22
|
+
post_stream = double
|
23
|
+
PostStream.stub(:new).with('b') { post_stream }
|
24
|
+
expect(streams.last).to eq(post_stream)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#count' do
|
30
|
+
it 'should return the count' do
|
31
|
+
posts = ['a', 'b']
|
32
|
+
streams = described_class.new(posts)
|
33
|
+
expect(streams.count).to eq(2)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'when streaming a scope' do
|
38
|
+
it 'should return the stream collection of that scope' do
|
39
|
+
blogs = ['a', 'b']
|
40
|
+
awesome_blogs = ['b']
|
41
|
+
blogs.stub(:awesome) { awesome_blogs }
|
42
|
+
streams = BlogsStream.new(blogs)
|
43
|
+
expect(streams.awesome.size).to eq(1)
|
44
|
+
expect(streams.awesome.first).to be_kind_of(BlogStream)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#to_a' do
|
49
|
+
it 'should return an array of stream objects' do
|
50
|
+
blogs = ['a', 'b']
|
51
|
+
streams = BlogsStream.new(blogs)
|
52
|
+
array = streams.to_a
|
53
|
+
expect(array).to be_kind_of(Array)
|
54
|
+
expect(array.first).to be_kind_of(BlogStream)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#each' do
|
59
|
+
it 'should allow looping through the elements of the stream' do
|
60
|
+
blogs = ['a', 'b']
|
61
|
+
streams = BlogsStream.new(blogs)
|
62
|
+
streams.each do |stream|
|
63
|
+
expect(stream).to be_kind_of(BlogStream)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
describe '.stream' do
|
70
|
+
context '`as` option is given' do
|
71
|
+
context 'result is not an enumerable' do
|
72
|
+
it 'should return the object instantiated in the stream class' do
|
73
|
+
image = double
|
74
|
+
Image.stub(:find).with('2') { image }
|
75
|
+
controller = double
|
76
|
+
images_stream = ImagesStream.new(nil, controller: controller)
|
77
|
+
expect(images_stream['2']).to be_kind_of(ImageStream)
|
78
|
+
expect(images_stream['2'].source).to eq(image)
|
79
|
+
expect(images_stream['2'].stream_context).to include(controller: controller)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module LiquidStream
|
2
|
+
class Utils
|
3
|
+
|
4
|
+
def self.stream_class_name_from(name)
|
5
|
+
"#{class_prefix_from(name).singularize}Stream"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.stream_class_from(name)
|
9
|
+
stream_class_name_from(name).constantize
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.streams_class_name_from(name)
|
13
|
+
"#{class_prefix_from(name).pluralize}Stream"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def self.class_prefix_from(name)
|
19
|
+
name = name.to_s if name.respond_to?(:to_s)
|
20
|
+
name =~ /^(\w+)Stream$/ ? $1 : name.classify
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LiquidStream::Utils do
|
4
|
+
|
5
|
+
describe '.stream_class_name_from' do
|
6
|
+
it 'should return the best-guess class name for the argument' do
|
7
|
+
expect(described_class.stream_class_name_from(:posts)).to eq('PostStream')
|
8
|
+
expect(described_class.stream_class_name_from(:post)).to eq('PostStream')
|
9
|
+
expect(described_class.stream_class_name_from(:blogs)).to eq('BlogStream')
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'name already contains Stream' do
|
13
|
+
it 'should return the text as is' do
|
14
|
+
expect(described_class.stream_class_name_from('PostsStream')).
|
15
|
+
to eq('PostStream')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'name is a collection stream class' do
|
20
|
+
it 'should return the singular version string' do
|
21
|
+
expect(described_class.stream_class_name_from(PostStream)).
|
22
|
+
to eq('PostStream')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.stream_class_from' do
|
28
|
+
it 'should return the constantized class of the best-guess class name for the arg' do
|
29
|
+
expect(described_class.stream_class_from(:posts)).to eq(PostStream)
|
30
|
+
expect(described_class.stream_class_from(:blog)).to eq(BlogStream)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.streams_class_name_from' do
|
35
|
+
it 'should return the pluralized best guess class name from the arg' do
|
36
|
+
expect(described_class.streams_class_name_from(:post)).
|
37
|
+
to eq('PostsStream')
|
38
|
+
expect(described_class.streams_class_name_from('BlogStream')).
|
39
|
+
to eq('BlogsStream')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'liquid_stream'
|
2
|
+
require 'rspec'
|
3
|
+
require 'active_model'
|
4
|
+
require 'pry'
|
5
|
+
require 'capybara'
|
6
|
+
SPEC_DIR = File.expand_path(File.dirname(__FILE__))
|
7
|
+
Dir["#{SPEC_DIR}/fixtures/**/*.rb"].each {|f| require(f)}
|
8
|
+
Dir["#{SPEC_DIR}/support/**/*.rb"].each {|f| require(f)}
|
9
|
+
|
10
|
+
STREAM_CLASSES = [BlogStream, PostStream]
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.order = 'random'
|
14
|
+
|
15
|
+
config.before(:each) do
|
16
|
+
STREAM_CLASSES.each(&:snapshot_streams!)
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after(:each) do
|
20
|
+
STREAM_CLASSES.each(&:restore_streams!)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module LiquidStream
|
2
|
+
class Stream
|
3
|
+
|
4
|
+
class_attribute :temporary_streams
|
5
|
+
|
6
|
+
def self.snapshot_streams!
|
7
|
+
self.temporary_streams = self.liquid_streams
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.restore_streams!
|
11
|
+
self.liquid_streams = self.temporary_streams
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: liquid_stream
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ramon Tayag
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activemodel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: capybara
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: liquid
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activesupport
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.0.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.0.0
|
111
|
+
description: Allows chaining of context aware Liquid drops
|
112
|
+
email:
|
113
|
+
- ramon.tayag@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- CHANGELOG.md
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- lib/liquid_stream.rb
|
125
|
+
- lib/liquid_stream/errors.rb
|
126
|
+
- lib/liquid_stream/stream.rb
|
127
|
+
- lib/liquid_stream/streams.rb
|
128
|
+
- lib/liquid_stream/version.rb
|
129
|
+
- liquid_stream.gemspec
|
130
|
+
- spec/fixtures/base.rb
|
131
|
+
- spec/fixtures/blog.rb
|
132
|
+
- spec/fixtures/blog_stream.rb
|
133
|
+
- spec/fixtures/blogs_stream.rb
|
134
|
+
- spec/fixtures/comment.rb
|
135
|
+
- spec/fixtures/image.rb
|
136
|
+
- spec/fixtures/image_stream.rb
|
137
|
+
- spec/fixtures/images_stream.rb
|
138
|
+
- spec/fixtures/post.rb
|
139
|
+
- spec/fixtures/post_stream.rb
|
140
|
+
- spec/fixtures/posts_stream.rb
|
141
|
+
- spec/integrations/allow_chaining_via_stream_spec.rb
|
142
|
+
- spec/liquid_stream/stream_spec.rb
|
143
|
+
- spec/liquid_stream/streams_spec.rb
|
144
|
+
- spec/liquid_stream/utils.rb
|
145
|
+
- spec/liquid_stream/utils_spec.rb
|
146
|
+
- spec/spec_helper.rb
|
147
|
+
- spec/support/streamer_resetter.rb
|
148
|
+
homepage: ''
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.0.3
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: Allow a more Ruby-like interface to Liquid drops with context awareness
|
172
|
+
test_files:
|
173
|
+
- spec/fixtures/base.rb
|
174
|
+
- spec/fixtures/blog.rb
|
175
|
+
- spec/fixtures/blog_stream.rb
|
176
|
+
- spec/fixtures/blogs_stream.rb
|
177
|
+
- spec/fixtures/comment.rb
|
178
|
+
- spec/fixtures/image.rb
|
179
|
+
- spec/fixtures/image_stream.rb
|
180
|
+
- spec/fixtures/images_stream.rb
|
181
|
+
- spec/fixtures/post.rb
|
182
|
+
- spec/fixtures/post_stream.rb
|
183
|
+
- spec/fixtures/posts_stream.rb
|
184
|
+
- spec/integrations/allow_chaining_via_stream_spec.rb
|
185
|
+
- spec/liquid_stream/stream_spec.rb
|
186
|
+
- spec/liquid_stream/streams_spec.rb
|
187
|
+
- spec/liquid_stream/utils.rb
|
188
|
+
- spec/liquid_stream/utils_spec.rb
|
189
|
+
- spec/spec_helper.rb
|
190
|
+
- spec/support/streamer_resetter.rb
|