magic-presenter 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2d9f852b05ac3df9f02d0a6f43256155d36f0aa54c9b94887017c6695040000
4
- data.tar.gz: 07f21527902b18f58d4ffc88655b3e37317a331fa8db2f37addaa421478a523b
3
+ metadata.gz: 04a88e0f222913a774946299ef036231cb1628baaa980111b5bb058cb77f69a4
4
+ data.tar.gz: 9a5d28343913ebae389a941153d49d6c3d04acb2e6e61891ada49404a2e1d49f
5
5
  SHA512:
6
- metadata.gz: 3d230e25c5b5a5bfaef1e941df5388cdcc71315287fbf71177fa7ebe1ca13ff4fa16a2e6a07e358bbed35213053262e6900b390cdc9572f5e33ea5ae59161626
7
- data.tar.gz: 36e3545b509b62c1c68f22d156adec1ef46ddf34ff0efeb9875aedf13bac1856a6387006211ad9c93a9dc118aa55f98a6a0db09133172d8b6453ed6f81e968e1
6
+ metadata.gz: 0a572d9c94e5b75cb65c23e029d524f19d48457d1ea57bf6185d4ed463757296c2917d77e7210ab67021347b111136e34c1cbcdb15ae47c6d67445623ff13508
7
+ data.tar.gz: 41c4b6afee8ffb1b89aedbba4f619d94ddc04d3c4201afa6ae7c34fec7d1fe52a746eb3fc29463d316fc446c4e1a7b9458395c473c18af02b388caae0f2dabaa
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # 🧙 Magic Presenter
2
2
 
3
+ ![GitHub Actions Workflow Status](
4
+ https://img.shields.io/github/actions/workflow/status/Alexander-Senko/magic-presenter/ci.yml
5
+ )
3
6
  ![Code Climate maintainability](
4
7
  https://img.shields.io/codeclimate/maintainability-percentage/Alexander-Senko/magic-presenter
5
8
  )
@@ -15,11 +18,15 @@ Based on [Magic Decorator](https://github.com/Alexander-Senko/magic-decorator),
15
18
 
16
19
  Install the gem and add to the application's Gemfile by executing:
17
20
 
18
- $ bundle add magic-presenter
21
+ $ bundle add magic-presenter
19
22
 
20
23
  If bundler is not being used to manage dependencies, install the gem by executing:
21
24
 
22
- $ gem install magic-presenter
25
+ $ gem install magic-presenter
26
+
27
+ After all the gems are `bundle`d run the installer in the project directory to generate the necessary files:
28
+
29
+ $ bin/rails generate magic:presenter:install
23
30
 
24
31
  ## Usage
25
32
 
@@ -51,7 +58,17 @@ When no presenter is found,
51
58
  - `#decorate!` raises `Magic::Lookup::Error`,
52
59
  - `#decorated` returns the original object.
53
60
 
54
- ## Magic
61
+ ### Generators
62
+
63
+ A generator can be used to generate a presenter:
64
+
65
+ $ bin/rails generate presenter Person
66
+
67
+ See the help for more info:
68
+
69
+ $ bin/rails generate presenter --help
70
+
71
+ ## 🧙 Magic
55
72
 
56
73
  It’s based on [Magic Decorator](
57
74
  https://github.com/Alexander-Senko/magic-decorator#magic
@@ -59,7 +76,7 @@ It’s based on [Magic Decorator](
59
76
 
60
77
  ### Presentable scope
61
78
 
62
- `Magic::Presentable` is mixed into `ActiveModel::Model` by default.
79
+ `Magic::Presentable` is mixed into `ActiveModel::API` by default.
63
80
  It means that any model, be it `ActiveRecord::Base`, `Mongoid::Document` or whatever else, is _magically presentable_.
64
81
 
65
82
  ### Presenter class inference
@@ -68,9 +85,24 @@ Presenters provide automatic class inference for any model based on its class na
68
85
  https://github.com/Alexander-Senko/magic-lookup
69
86
  ).
70
87
 
71
- For example, `MyNamespace::MyModel.new.decorate` looks for `MyNamespace::MyModelPresenter` first.
88
+ For example, `MyNamespace::MyModel.new.decorate` looks for `MyNamespace::MyPresenter` first.
72
89
  When missing, it further looks for decorators for its ancestor classes, up to `ObjectPresenter`.
73
90
 
91
+ #### Mapping rules
92
+
93
+ - `MyObject` → `MyObjectPresenter`
94
+ - `MyModel` → `MyPresenter`
95
+ - `MyRecord` → `MyPresenter`
96
+
97
+ > [!TIP]
98
+ > That’s why `ApplicationPresenter` presents `ApplicationRecord` alongside all its descendants automagically with no extra code.
99
+
100
+ When in doubt, one can use `Magic::Presenter.name_for`:
101
+
102
+ ```ruby
103
+ Magic::Presenter.name_for Person # => "PersonPresenter"
104
+ ```
105
+
74
106
  ## Development
75
107
 
76
108
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.to_prepare do
4
+ [ Rails.root / 'app/presenters' ]
5
+ .select(&:exist?)
6
+ .each { Rails.autoloaders.main.eager_load_dir _1 }
7
+ end unless Rails.application.config.eager_load
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ActiveSupport.on_load :active_model do
4
- include Magic::Presentable
3
+ Rails.application.config.to_prepare do
4
+ ActiveModel::API.include Magic::Presentable
5
5
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure do |config|
4
+ config.include concern(:PresenterExampleGroup) {
5
+ included { metadata[:type] = :presenter }
6
+ }, file_path: %r'spec/presenters', type: :presenter
7
+ end if defined? RSpec
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magic
4
+ module Presenter
5
+ module Generator # :nodoc:
6
+ require 'generators/presenter/presenter_generator'
7
+
8
+ private
9
+
10
+ def file_name name = super()
11
+ name
12
+ .camelize
13
+ .then { Magic::Presenter.name_for _1 }
14
+ .underscore
15
+ end
16
+
17
+ def file_path path = super(), root: target_root
18
+ root / 'presenters' / path
19
+ end
20
+
21
+ def presenter_path(*) = file_path(*, root: PresenterGenerator.target_root)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates application files used by Magic Presenter:
3
+ ApplicationPresenter: a base class for other presenters to
4
+ inherit from.
5
+
6
+ Example:
7
+ bin/rails generate magic:presenter:install
8
+
9
+ This will create:
10
+ ApplicationPresenter: app/presenters/application_presenter.rb
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magic
4
+ module Presenter
5
+ class InstallGenerator < Rails::Generators::Base # :nodoc:
6
+ include Generator
7
+
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ def create_files
11
+ template 'application_presenter.rb', "#{presenter_path namespaced_file_name 'ApplicationRecord'}.rb"
12
+ end
13
+
14
+ private
15
+
16
+ def namespaced_file_name(*)
17
+ File.join *(namespaced_path if namespaced?), file_name(*)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ <% module_namespacing do -%>
2
+ class ApplicationPresenter < Magic::Presenter::Base
3
+ # Define presentation-specific methods here.
4
+ # They are shared by all the descendant presenters.
5
+ end
6
+ <% end -%>
@@ -0,0 +1,28 @@
1
+ Description:
2
+ Generates a new presenter for a model. Pass the model name, either
3
+ CamelCased or under_scored, as an argument.
4
+
5
+ This generator invokes your configured test framework, which
6
+ defaults to TestUnit. It supports both TestUnit and RSpec.
7
+
8
+ If a `--parent` option is given, it’s used as a superclass of the
9
+ created presenter.
10
+
11
+ Example:
12
+ bin/rails generate presenter account
13
+
14
+ This will create:
15
+ Presenter: app/presenters/account_presenter.rb
16
+ for TestUnit:
17
+ Test: test/presenters/account_presenter_test.rb
18
+ for RSpec:
19
+ Spec: spec/presenters/account_presenter_spec.rb
20
+
21
+ bin/rails generate presenter admin/account
22
+
23
+ This will create:
24
+ Presenter: app/presenters/admin/account_presenter.rb
25
+ for TestUnit:
26
+ Test: test/presenters/admin/account_presenter_test.rb
27
+ for RSpec:
28
+ Spec: spec/presenters/admin/account_presenter_spec.rb
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PresenterGenerator < Rails::Generators::NamedBase # :nodoc:
4
+ include Magic::Presenter::Generator
5
+
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ check_class_collision suffix: 'Presenter'
9
+
10
+ class_option :parent,
11
+ type: :string,
12
+ default: 'ApplicationPresenter',
13
+ desc: 'The parent class for the generated presenter'
14
+
15
+ cattr_reader :target_root, default: Pathname('app')
16
+
17
+ def create_presenter_file
18
+ template 'presenter.rb', "#{file_path}.rb"
19
+ end
20
+
21
+ hook_for :test_framework
22
+
23
+ private
24
+
25
+ def parent_class_name = options[:parent].classify
26
+ end
@@ -0,0 +1,5 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %> < <%= parent_class_name %>
3
+ # Define presentation-specific methods here.
4
+ end
5
+ <% end -%>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'generators/rspec'
4
+ require 'generators/magic/presenter/generator'
5
+
6
+ module Rspec # :nodoc:
7
+ module Generators # :nodoc:
8
+ class PresenterGenerator < Base # :nodoc:
9
+ include Magic::Presenter::Generator
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ cattr_reader :target_root, default: Pathname('spec')
14
+
15
+ def create_test_file
16
+ template 'presenter_spec.rb', "#{file_path}_spec.rb"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ require 'rails_helper'
2
+
3
+ <% module_namespacing do -%>
4
+ RSpec.describe <%= class_name %> do
5
+ pending "add some examples to (or delete) #{__FILE__}"
6
+ end
7
+ <% end -%>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/test_unit'
4
+ require 'generators/magic/presenter/generator'
5
+
6
+ module TestUnit # :nodoc:
7
+ module Generators # :nodoc:
8
+ class PresenterGenerator < Base # :nodoc:
9
+ include Magic::Presenter::Generator
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ check_class_collision suffix: 'PresenterTest'
14
+
15
+ cattr_reader :target_root, default: Pathname('test')
16
+
17
+ def create_test_file
18
+ template 'presenter_test.rb', "#{file_path}_test.rb"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ require "test_helper"
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>Test < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ <% end -%>
@@ -9,6 +9,6 @@ module Magic
9
9
 
10
10
  private
11
11
 
12
- def decorator = Presenter.for self.class
12
+ def decorator_base = Presenter
13
13
  end
14
14
  end
@@ -13,12 +13,18 @@ module Magic
13
13
  #
14
14
  # Presenters provide automatic class inference for any model based
15
15
  # on its class name powered by Magic Lookup.
16
- # For example, `MyModel.new` looks for `MyModelPresenter` first.
16
+ # For example, `MyModel.new` looks for `MyPresenter` first.
17
17
  # If not found, it looks for presenters of its ancestor classes,
18
18
  # up to `ObjectPresenter`.
19
19
  class Base < Decorator::Base
20
20
  class << self
21
- def name_for(object_class) = "#{object_class}Presenter"
21
+ def name_for object_class
22
+ object_class
23
+ .to_s
24
+ .delete_suffix('Model')
25
+ .delete_suffix('Record')
26
+ .then { "#{_1}Presenter" }
27
+ end
22
28
  end
23
29
  end
24
30
  end
@@ -4,6 +4,10 @@ module Magic
4
4
  module Presenter
5
5
  class Engine < ::Rails::Engine # :nodoc:
6
6
  isolate_namespace Magic::Presenter
7
+
8
+ config.generators do
9
+ _1.test_framework = :rspec
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Magic
4
4
  module Presenter
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -5,12 +5,13 @@ require 'magic/decorator'
5
5
  require_relative 'presenter/version'
6
6
  require_relative 'presenter/engine'
7
7
 
8
- module Magic
8
+ module Magic # :nodoc:
9
9
  autoload :Presentable, 'magic/presentable'
10
10
 
11
11
  # Presentation layer for Rails models
12
12
  module Presenter
13
- autoload :Base, 'magic/presenter/base'
13
+ autoload :Base, 'magic/presenter/base'
14
+ autoload :Generator, 'generators/magic/presenter/generator'
14
15
 
15
16
  module_function
16
17
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magic-presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Senko
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2024-10-19 00:00:00.000000000 Z
10
+ date: 2024-10-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: '0'
38
+ version: 0.3.0.alpha
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: 0.3.0.alpha
46
46
  description: Based on Magic Decorator, it’s meant to replace Draper.
47
47
  email:
48
48
  - Alexander.Senko@gmail.com
@@ -53,7 +53,20 @@ files:
53
53
  - MIT-LICENSE
54
54
  - README.md
55
55
  - Rakefile
56
+ - config/initializers/loading.rb
56
57
  - config/initializers/presentable.rb
58
+ - config/initializers/rspec.rb
59
+ - lib/generators/magic/presenter/generator.rb
60
+ - lib/generators/magic/presenter/install/USAGE
61
+ - lib/generators/magic/presenter/install/install_generator.rb
62
+ - lib/generators/magic/presenter/install/templates/application_presenter.rb.tt
63
+ - lib/generators/presenter/USAGE
64
+ - lib/generators/presenter/presenter_generator.rb
65
+ - lib/generators/presenter/templates/presenter.rb.tt
66
+ - lib/generators/rspec/presenter/presenter_generator.rb
67
+ - lib/generators/rspec/presenter/templates/presenter_spec.rb.tt
68
+ - lib/generators/test_unit/presenter/presenter_generator.rb
69
+ - lib/generators/test_unit/presenter/templates/presenter_test.rb.tt
57
70
  - lib/magic/presentable.rb
58
71
  - lib/magic/presenter.rb
59
72
  - lib/magic/presenter/authors.rb
@@ -67,7 +80,7 @@ licenses:
67
80
  metadata:
68
81
  homepage_uri: https://github.com/Alexander-Senko/magic-presenter
69
82
  source_code_uri: https://github.com/Alexander-Senko/magic-presenter
70
- changelog_uri: https://github.com/Alexander-Senko/magic-presenter/blob/v0.1.0/CHANGELOG.md
83
+ changelog_uri: https://github.com/Alexander-Senko/magic-presenter/blob/v0.2.0/CHANGELOG.md
71
84
  rdoc_options: []
72
85
  require_paths:
73
86
  - lib