livery 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9346fcf616e20f17e894e0067d5608c37f6e616ed4d9e32cc493cc5b389e10e3
4
+ data.tar.gz: 22d43934954cc145d5c0b3feec6c8513790de32b58b43f55cca8188b850f3b5b
5
+ SHA512:
6
+ metadata.gz: ae804d9cbed9af03626966850e162f31e164ba7f011d6b6b2e71d2351ad8c78593a115c9c4f4926a6ecda92faff360065a0fb2e889c451d19644fc647383a228
7
+ data.tar.gz: 96ceffc94eaeb28529663c5e2a17ce58ff8bfe9fdbb4af0ade4bbd608fb120c7fd019a5b6e7ae99e11ec59a8e818f8507dd8394284a1b6cba5fa8c174d9149fa
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in livery.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ livery (0.1.0)
5
+ activesupport (~> 6.0.0.rc1)
6
+ i18n (>= 0.7, < 2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (6.0.0.rc1)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ zeitwerk (~> 2.1, >= 2.1.4)
17
+ concurrent-ruby (1.1.5)
18
+ diff-lcs (1.3)
19
+ i18n (1.6.0)
20
+ concurrent-ruby (~> 1.0)
21
+ minitest (5.11.3)
22
+ rake (10.5.0)
23
+ rspec (3.8.0)
24
+ rspec-core (~> 3.8.0)
25
+ rspec-expectations (~> 3.8.0)
26
+ rspec-mocks (~> 3.8.0)
27
+ rspec-core (3.8.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-expectations (3.8.3)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-mocks (3.8.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-support (3.8.0)
36
+ thread_safe (0.3.6)
37
+ tzinfo (1.2.5)
38
+ thread_safe (~> 0.1)
39
+ zeitwerk (2.1.6)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ bundler (~> 1.16)
46
+ livery!
47
+ rake (~> 10.0)
48
+ rspec (~> 3.0)
49
+
50
+ BUNDLED WITH
51
+ 1.16.3
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019 Freebird, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Livery
2
+
3
+ A simple presenter library for Rails.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'livery'
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Presenter classes that wrap models should be named
16
+ `[ModelName]Presenter`, and inherit from `Livery::Presenter`.
17
+
18
+ The underlying model object is called a `resource`, and is passed in
19
+ initialization. Let's say we have a `Post` model:
20
+
21
+ ```ruby
22
+ # app/models/post.rb
23
+
24
+ class Post < ApplicationRecord
25
+ # title: string, body: text
26
+ belongs_to :author
27
+ end
28
+ ```
29
+
30
+ An empty `PostPresenter` would look like:
31
+
32
+ ```ruby
33
+ # app/presenters/post_presenter.rb
34
+
35
+ class PostPresenter < Livery::Presenter
36
+ resource :post
37
+ end
38
+ ```
39
+
40
+ By default, no methods are added except `resource` and, as an alias,
41
+ the name of the resource; in this case `post`. So you should define
42
+ methods or explicitly delegate them to the underlying object:
43
+
44
+ ```ruby
45
+ # app/presenters/post_presenter.rb
46
+
47
+ class PostPresenter < Livery::Presenter
48
+ resource :post
49
+
50
+ delegate :title,
51
+ :body,
52
+ to: :post
53
+
54
+ def posted_at
55
+ if post.created_at.present?
56
+ post.created_at.strftime('%a, %b %d %Y')
57
+ else
58
+ 'Not posted yet'
59
+ end
60
+ end
61
+ end
62
+
63
+ # example:
64
+ post = Post.new title: 'An aviation point of view'
65
+ post_presenter = PostPresenter.new(post)
66
+ post_presenter.title # => 'An aviation point of view'
67
+ post_presenter.posted_at # => 'Not posted yet'
68
+ ```
69
+
70
+ ### Controllers
71
+
72
+ Livery provides some convenience methods to wrap model objects with
73
+ presenters, and to ensure that only presenters are passed to the view:
74
+
75
+ ```ruby
76
+ # app/controllers/application_controller.rb
77
+
78
+ class ApplicationController < ActionController::Base
79
+ include Livery::Controller # add this line
80
+ end
81
+ ```
82
+
83
+ The `use_presenters!` method overrides `view_assigns` so that instance
84
+ variables are not passed to views. It also defines a `present()`
85
+ method to explicitly pass them:
86
+
87
+ ```ruby
88
+ # app/controllers/posts_controller.rb
89
+
90
+ class PostsController < ApplicationController
91
+ use_presenters!
92
+
93
+ def show
94
+ @post = Post.find(params[:id])
95
+ present(current_post: @post)
96
+ end
97
+ end
98
+ ```
99
+
100
+ `present` will find the `PostPresenter` class to wrap the `@post`
101
+ model object, and assign it to the `@current_post` instance variable
102
+ in the view:
103
+
104
+ ```erb
105
+ # app/views/posts/show.html.erb:
106
+
107
+ <h1><%= @current_post.title ></h1>
108
+ <h2>Posted at <%= @current_post.posted_at %></h2>
109
+ ```
110
+
111
+ ### Presenter associations
112
+
113
+ Given an associated model (`post.author`) with a presenter class:
114
+
115
+ ```ruby
116
+ class AuthorPresenter < Livery::Presenter
117
+ resource :author
118
+
119
+ def full_name
120
+ author.given_name + ' ' + author.surname
121
+ end
122
+ end
123
+ ```
124
+
125
+ you can use `presenter_association`:
126
+
127
+ ```ruby
128
+ class PostPresenter < Livery::Presenter
129
+ resource :post
130
+
131
+ presenter_association :author
132
+
133
+ # ...
134
+ end
135
+
136
+ PostPresenter.new(post).author.full_name # => "Amelia Earhart"
137
+ ```
138
+
139
+ ## Development
140
+
141
+ Run `bin/rake spec` to run the tests.
142
+
143
+ To install this gem onto your local machine, run `bundle exec rake install`.
144
+
145
+ To release a new version, update the version number in `version.rb`,
146
+ and then run `bundle exec rake release`, which will create a git tag
147
+ for the version, push git commits and tags, and push the `.gem` file
148
+ to [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "livery"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/rake ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rake", "rake")
data/bin/rspec ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,20 @@
1
+ module Livery
2
+ module Controller
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def use_presenters!
7
+ define_method "view_assigns" do
8
+ @_presenters || {}
9
+ end
10
+ end
11
+ end
12
+
13
+ def present(hsh)
14
+ @_presenters ||= {}
15
+ hsh.each_with_object(@_presenters) do |(k, v), acc|
16
+ acc[k] = Livery::Presenter.to_presenter(v)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,90 @@
1
+ module Livery
2
+ class Presenter
3
+ attr_reader :resource
4
+
5
+ def initialize(resource, options = {})
6
+ @resource = resource
7
+ options.each do |k, v|
8
+ raise ArgumentError, "cannot initialize `resource` variable" if k.to_s == "resource"
9
+ instance_variable_set("@#{k}", v)
10
+ end
11
+ end
12
+
13
+ class << self
14
+ def formatted_datetime(*names)
15
+ names.each do |name|
16
+ define_method name do
17
+ value = @resource.public_send(name)
18
+ I18n.l(value)
19
+ end
20
+ end
21
+ end
22
+
23
+ def method_added(sym)
24
+ raise "can't override resource on a subclass of Presenter" if sym == :resource
25
+ end
26
+
27
+ def presenter_association(*names, source: nil, namespace: nil)
28
+ names.each do |name|
29
+ instance_variable_name = "@#{name}".to_sym
30
+
31
+ define_method name do
32
+ return instance_variable_get(instance_variable_name) if instance_variable_defined?(instance_variable_name)
33
+
34
+ association_resource = source.present? ? @resource.instance_exec(&source) : @resource.public_send(name)
35
+
36
+ association_presenter = Presenter.to_presenter(association_resource, namespace: namespace)
37
+
38
+ instance_variable_set(instance_variable_name, association_presenter)
39
+ association_presenter
40
+ end
41
+ end
42
+ end
43
+
44
+ def presenterize(klass, namespace: nil)
45
+ [namespace.presence, "#{klass}Presenter"].compact.join('::').constantize
46
+ end
47
+
48
+ def resource(sym)
49
+ define_method sym do
50
+ @resource
51
+ end
52
+ end
53
+
54
+ def to_presenter(object, namespace: nil)
55
+ return object.map { |o| to_presenter_single(o, namespace: namespace) } if object.is_a?(Enumerable)
56
+ to_presenter_single(object, namespace: namespace)
57
+ end
58
+
59
+ private
60
+
61
+ def to_presenter_single(object, namespace: nil)
62
+ return nil if object.nil?
63
+
64
+ klass = object.class
65
+
66
+ if klass < Presenter
67
+ object
68
+ else
69
+ presenterize(klass, namespace: namespace).new(object)
70
+ end
71
+ end
72
+ end
73
+
74
+ def t(*args)
75
+ I18n.t(self.class.name.underscore + args.shift, *args)
76
+ end
77
+
78
+ def t!(*args)
79
+ I18n.t!(self.class.name.underscore + args.shift, *args)
80
+ end
81
+
82
+ def to_model
83
+ raise 'Presenter objects should not be used for forms. Call .resource on this Presenter'
84
+ end
85
+
86
+ def to_param(*args)
87
+ @resource.to_param(*args)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ module Livery
2
+ module RSpec
3
+ module Helper
4
+ def presenter_receives_instance_doubles!
5
+ before(:each) do
6
+ allow(Livery::Presenter).to receive(:to_presenter_single) do |obj, namespace: nil|
7
+ next nil unless obj.present?
8
+
9
+ klass = obj.instance_variable_get(:@doubled_module).target
10
+ Livery::Presenter.presenterize(klass, namespace: namespace).new(obj)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Livery
2
+ VERSION = "0.1.0"
3
+ end
data/lib/livery.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "active_support/all"
2
+
3
+ require "livery/version"
4
+ require "livery/presenter"
5
+ require "livery/controller"
6
+
7
+ module Livery
8
+ end
data/livery.gemspec ADDED
@@ -0,0 +1,34 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "livery/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "livery"
8
+ spec.version = Livery::VERSION
9
+ spec.authors = ["Michael Hoy", "Kevin Mannix", "John Russell"]
10
+ spec.email = ["dev@getfreebird.com"]
11
+
12
+ spec.summary = %q{A library for presenter objects for use in Rails.}
13
+ spec.description = %q{A library for presenter objects for use in Rails.}
14
+ spec.homepage = "https://www.getfreebird.com"
15
+ spec.license = "MIT"
16
+
17
+ spec.required_ruby_version = ">= 2.5.0"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test/|spec/|features/|.ruby-version)}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.16"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+
32
+ spec.add_dependency "activesupport", "~> 6.0.0.rc1"
33
+ spec.add_dependency "i18n", ">= 0.7", "< 2"
34
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: livery
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Hoy
8
+ - Kevin Mannix
9
+ - John Russell
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2019-06-28 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.16'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.16'
29
+ - !ruby/object:Gem::Dependency
30
+ name: rake
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '10.0'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '10.0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rspec
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '3.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: activesupport
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 6.0.0.rc1
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 6.0.0.rc1
71
+ - !ruby/object:Gem::Dependency
72
+ name: i18n
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0.7'
78
+ - - "<"
79
+ - !ruby/object:Gem::Version
80
+ version: '2'
81
+ type: :runtime
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0.7'
88
+ - - "<"
89
+ - !ruby/object:Gem::Version
90
+ version: '2'
91
+ description: A library for presenter objects for use in Rails.
92
+ email:
93
+ - dev@getfreebird.com
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - Gemfile
101
+ - Gemfile.lock
102
+ - LICENSE
103
+ - README.md
104
+ - Rakefile
105
+ - bin/console
106
+ - bin/rake
107
+ - bin/rspec
108
+ - bin/setup
109
+ - lib/livery.rb
110
+ - lib/livery/controller.rb
111
+ - lib/livery/presenter.rb
112
+ - lib/livery/rspec/helper.rb
113
+ - lib/livery/version.rb
114
+ - livery.gemspec
115
+ homepage: https://www.getfreebird.com
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: 2.5.0
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubygems_version: 3.0.3
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: A library for presenter objects for use in Rails.
138
+ test_files: []