golden-objects 0.1.0 → 0.3.3

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: 4854525e660454fcdcd717c48e05addb718eb6571e7ee2203068587968bc570d
4
- data.tar.gz: b12b5850a5da5b637fe56005acf318a22085347dd9a22483dfc52bd24204978e
3
+ metadata.gz: 0d4ee2db8852f5eddb965ed6707deb5c5e2379c75d31fc60ef1137164673841d
4
+ data.tar.gz: 8e1db0713227091ab46b1c6ab73795861711ba8317c2cd35b9145533bfe308f9
5
5
  SHA512:
6
- metadata.gz: ab570549fa989c52516de15a1c87a2439f3aba9928ef5563f9c9e02e82daa270c88f8c55cbd9936eaff22c23a6147a731d3bb3299f56b96fc17cbf50fb50324d
7
- data.tar.gz: d149d32f6989842cb7407323d56a160651cb820f030f5b9ce5b649bfefa6ca7202ed5892e18e23745b3a9bb7c9fc9c6d6826e0088904ce0bf877bdc57bee693c
6
+ metadata.gz: f53b0282f9ec87265125ffaafb2078872d67ad90464abc24039fa345031b010fe2c6e363b8bd17b405113224d5df8001e0b19c77bce83a9d9626ad662f39b21e
7
+ data.tar.gz: 7f804752ea7aa38429115444da94cf227b2c3a273b05da8c7c70b5605084a3488cb51004e62ee6aa9d20cc2c2100f939a8a922b804b1405a11017fd682eb7714
@@ -1,5 +1,38 @@
1
1
  # Change Logs
2
2
 
3
- ### v0.1.0
3
+ # v0.3.3
4
+
5
+ * fix typo of `total_pages`.
6
+ * Add more methods for pagination.
7
+
8
+ ## v0.3.2
9
+
10
+ * Add `Golden::QueryRecordPresenter` and `Golden::QueryFormPresenter` as basic presenters.
11
+
12
+ ## v0.3.1
13
+
14
+ * The argement `presenter_class` of `Golden::QueryResultPresenter.collect` should be string instead of class.
15
+
16
+ ## v0.3.0
17
+
18
+ * `Golden::QueryResultPresenter`:
19
+ * BREAKING: Change `initialize` definition.
20
+ * Add `records`, `presenter_class` accessors.
21
+ * Add `collect`, `paginated_array` class methods.
22
+ * Let `presenters` can be customized.
23
+ * Add missing `total_page` method.
24
+ * `Golden::QueryFormOperator`:
25
+ * Add `mode` accessor and pass to form.
26
+ * Let `query_result`, `record_presenter_class` can be customized.
27
+ * `Golden::QueryContext`:
28
+ * Include `ActiveRecord::Sanitization::ClassMethods`.
29
+ * Change prefered `sort` implementation.
30
+
31
+ ## v0.2.0
32
+
33
+ * Add golden form builder and helper.
34
+ * Golden query context support pluck.
35
+
36
+ ## v0.1.0
4
37
 
5
38
  * Initial release.
data/README.md CHANGED
@@ -8,32 +8,49 @@ Let's compose business logics as ruby components to improve efficiency of mainte
8
8
 
9
9
  Add this line to your application's Gemfile:
10
10
 
11
- ```ruby
11
+ ``` ruby
12
12
  gem 'golden-objects'
13
13
  ```
14
14
 
15
15
  And then execute:
16
16
 
17
- $ bundle install
17
+ ``` shell
18
+ bundle install
19
+ ```
18
20
 
19
21
  Or install it yourself as:
20
22
 
21
- $ gem install golden-objects
23
+ ``` shell
24
+ gem install golden-objects
25
+ ```
22
26
 
23
27
  ## Usage
24
28
 
25
- Create your class and inherite appropriate golden object directly or thought another class.
29
+ Create your class and inherite appropriate golden object directly or though another class.
26
30
 
27
- ```
31
+ ``` ruby
28
32
  class ApplicationPresenter < Golden::ApplicationPresenter
29
33
  include Rails.application.routes.url_helpers
30
34
  end
31
35
 
36
+ class CartLineItemPresenter < ApplicationPresenter
37
+ class << self
38
+ def collect(records)
39
+ ::Golden::QueryResultPresenter.collect(records, name)
40
+ end
41
+ end
42
+
43
+ def initialize(line_item, accessors = {})
44
+ super(accessors)
45
+ @line_item = line_item
46
+ end
47
+ end
48
+
32
49
  class CartPresenter < ApplicationPresenter
33
50
  attr_accessor :product_variants
34
51
 
35
52
  def line_items_presenter
36
- @line_items_presenter ||= CartLineItemPresenter.all(product_variants: product_variants)
53
+ @line_items_presenter ||= CartLineItemPresenter.collect(product_variants)
37
54
  end
38
55
  end
39
56
  ```
@@ -45,14 +62,14 @@ And put into suggested pathes.
45
62
 
46
63
  Grouping classes by multi layers of folders are also suggested.
47
64
 
48
- ```
65
+ ``` shell
49
66
  app/components/[major business logic]/[secondary business logic]/xxx_xxx.rb
50
67
  app/objects/[major data model logic]/[secondary data model logic]/xxx_xxx.rb
51
68
  ```
52
69
 
53
70
  For example:
54
71
 
55
- ```
72
+ ``` shell
56
73
  app/components/identity/profile/update_form.rb
57
74
  app/components/frontend/popup_cart/main_presenter.rb
58
75
  app/objects/orders/line_item/create_operator.rb
@@ -61,15 +78,16 @@ app/objects/orders/create_operator.rb
61
78
 
62
79
  ## Modules
63
80
 
64
- 3 kinds of modules are provied.
81
+ 4 kinds of modules are provided.
65
82
 
66
83
  * attribute accessors
67
84
  * active model concerns
68
85
  * active record concerns
86
+ * action view extensions
69
87
 
70
88
  ## Objects
71
89
 
72
- 3 groups of objects are provied.
90
+ 3 groups of objects are provided.
73
91
 
74
92
  * Application objects
75
93
  * Golden::ApplicationCalculator
@@ -90,6 +108,18 @@ app/objects/orders/create_operator.rb
90
108
  * Golden::QueryFormOperator
91
109
  * Golden::QueryResultPresenter
92
110
 
111
+ ## Action View
112
+
113
+ require extension for actionview in controller of rails.
114
+
115
+ ``` ruby
116
+ require 'golden/action_view/extension'
117
+
118
+ class ApplicationController < ActionController::Base
119
+ helper ::Golden::FormHelper
120
+ end
121
+ ```
122
+
93
123
  ## Development
94
124
 
95
125
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -98,4 +128,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
98
128
 
99
129
  ## Contributing
100
130
 
101
- Bug reports and pull requests are welcome on GitHub at https://github.com/goldenio/golden-objects.
131
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/goldenio/golden-objects>.
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  # spec.metadata['allowed_push_host'] = 'https://github.com'
16
16
  spec.metadata['homepage_uri'] = spec.homepage
17
17
  spec.metadata['source_code_uri'] = 'https://github.com/goldenio/golden-objects.git'
18
- spec.metadata['changelog_uri'] = 'https://github.com/goldenio/golden-objects/CHANGELOG.md'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/goldenio/golden-objects/blob/master/CHANGELOG.md'
19
19
 
20
20
  # Specify which files should be added to the gem when it is released.
21
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -0,0 +1,2 @@
1
+ require 'golden/action_view/form_builder'
2
+ require 'golden/action_view/form_helper'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Golden
4
+ class FormBuilder < ActionView::Helpers::FormBuilder
5
+ def label(method, text = nil, options = {}, &block)
6
+ text ||= I18n.t("helpers.label.#{object.model_name.i18n_key}.#{method}")
7
+ super(method, text, options, &block)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Golden
4
+ module FormHelper
5
+ def golden_form_with(**options, &block)
6
+ model = options[:model]
7
+ default_options = { builder: ::Golden::FormBuilder }
8
+ default_options.merge!({ scope: :query, url: model.query_url }) if model.respond_to? :query_url
9
+ form_with(**default_options.merge(options), &block)
10
+ end
11
+ end
12
+ end
@@ -4,6 +4,7 @@ require 'golden/attribute_accessors'
4
4
 
5
5
  module Golden
6
6
  class ApplicationPresenter
7
+ extend ActiveModel::Naming
7
8
  extend ActiveModel::Translation
8
9
  include ActiveModel::AttributeAssignment
9
10
 
@@ -9,16 +9,23 @@ module Golden
9
9
  end
10
10
  end
11
11
 
12
+ include ::ActiveRecord::Sanitization::ClassMethods
13
+
12
14
  attr_accessor :page, :per
13
15
  attr_accessor :sort_field, :sort_direction
14
16
 
17
+ boolean_accessor :plucking
18
+ attr_reader :pluck_fields
19
+
15
20
  def initialize(accessors = {})
16
21
  assign_attributes(accessors || {})
17
22
  @page ||= 1
18
23
  @per ||= 100
19
24
  end
20
25
 
21
- def perform(mode = :paginated)
26
+ def perform(mode = :paginated, pluck: nil)
27
+ @plucking = pluck.present?
28
+ @pluck_fields = pluck
22
29
  send("find_#{mode}")
23
30
  end
24
31
 
@@ -47,7 +54,8 @@ module Golden
47
54
  def find_all
48
55
  return [] if invalid?
49
56
 
50
- relations.where(query_scopes).order(sort).all
57
+ finder = relations.where(query_scopes).order(sort).all
58
+ find_or_pluck(finder)
51
59
  end
52
60
 
53
61
  def find_count
@@ -59,7 +67,8 @@ module Golden
59
67
  def find_paginated
60
68
  return paginated_blank_result if invalid?
61
69
 
62
- relations.where(query_scopes).order(sort).page(page).per(per)
70
+ finder = relations.where(query_scopes).order(sort).page(page).per(per)
71
+ find_or_pluck(finder)
63
72
  end
64
73
 
65
74
  def paginated_blank_result
@@ -68,6 +77,22 @@ module Golden
68
77
  raise NotImplementedError
69
78
  end
70
79
 
80
+ def find_or_pluck(finder)
81
+ return finder unless plucking?
82
+
83
+ finder.pluck(safe_table_fields(pluck_fields) || Arel.star)
84
+ end
85
+
86
+ def safe_table_fields(table_and_fields)
87
+ table_fields = table_and_fields.flat_map do |table, fields|
88
+ table_name = send(table)&.name
89
+ next if table_name.blank?
90
+
91
+ fields.map { |field| [table_name, field.to_s].join('.') }
92
+ end.compact
93
+ Arel.sql(table_fields.join(', '))
94
+ end
95
+
71
96
  def relations
72
97
  raise NotImplementedError, <<~ERROR
73
98
  Please define #{__method__} like
@@ -86,7 +111,17 @@ module Golden
86
111
  def #{__method__}
87
112
  @sort_field ||= :id
88
113
  @sort_direction ||= :desc
89
- @#{__method__} ||= Record.arel_table[@sort_field].send(@sort_direction)
114
+ @#{__method__} = send("sort_by_\#{@sort_field}")
115
+ rescue NoMethodError
116
+ @#{__method__} = sort_by_id
117
+ end
118
+ ```
119
+ And define sort_by_xxx like
120
+ ```
121
+ def sort_by_id
122
+ [
123
+ Record.arel_table[sort_field].send(sort_direction)
124
+ ]
90
125
  end
91
126
  ```
92
127
  ERROR
@@ -97,6 +132,7 @@ module Golden
97
132
  Please define #{__method__} like
98
133
  ```
99
134
  def #{__method__}
135
+ @scopes = nil
100
136
  concat_xxx_scope
101
137
  @scopes
102
138
  end
@@ -105,10 +141,14 @@ module Golden
105
141
  ```
106
142
  def concat_xxx_scope
107
143
  scope = Record.arel_table[:xxx_number].eq(@xxx_number)
108
- @scopes = @scopes ? @scopes.and(scope) : scope
144
+ scopes_and(scope)
109
145
  end
110
146
  ```
111
147
  ERROR
112
148
  end
149
+
150
+ def scopes_and(scope)
151
+ @scopes = @scopes ? @scopes.and(scope) : scope
152
+ end
113
153
  end
114
154
  end
@@ -17,6 +17,7 @@ module Golden
17
17
 
18
18
  attr_reader :result
19
19
  attr_accessor :per, :page, :sort
20
+ attr_accessor :mode
20
21
 
21
22
  def initialize(params, accessors = {})
22
23
  @query_params = find_query_from(params)
@@ -25,6 +26,7 @@ module Golden
25
26
  @per ||= params.dig(:per)
26
27
  @page ||= params.dig(:page)
27
28
  @sort ||= params.dig(:sort)
29
+ @mode ||= :paginated
28
30
  end
29
31
 
30
32
  def save
@@ -48,12 +50,22 @@ module Golden
48
50
 
49
51
  def query_accessors
50
52
  attrs = attributes.clone
53
+ attrs.delete(:mode)
51
54
  attrs.each { |accessor, value| attrs.delete(accessor) if value.blank? }
52
55
  attrs
53
56
  end
54
57
 
55
58
  def query!
56
- raise NotImplementedError
59
+ raise NotImplementedError, <<~ERROR
60
+ Please define #{__method__} like
61
+ ```
62
+ def #{__method__}
63
+ context = ::Orders::QueryContext.new query_accessors
64
+ @result = context.perform(mode)
65
+ errors.merge! context.errors
66
+ end
67
+ ```
68
+ ERROR
57
69
  end
58
70
  end
59
71
  end
@@ -4,7 +4,7 @@ module Golden
4
4
  class QueryFormOperator < Golden::ApplicationOperator
5
5
  class << self
6
6
  # rubocop:disable Naming/PredicateName
7
- def has_query_form(form: nil, form_presenter: nil, result_presenter: nil)
7
+ def has_query_form(form: nil, form_presenter: nil, result_presenter: nil, record_presenter: nil)
8
8
  define_method :form_class do
9
9
  return form.to_s.constantize if form.present?
10
10
 
@@ -22,6 +22,12 @@ module Golden
22
22
 
23
23
  "#{self.class.parent.name}::QueryResultPresenter".constantize
24
24
  end
25
+
26
+ define_method :record_presenter_class do
27
+ return record_presenter.to_s.constantize if record_presenter.present?
28
+
29
+ "#{self.class.parent.name}::QueryRecordPresenter".constantize
30
+ end
25
31
  end
26
32
  # rubocop:enable Naming/PredicateName
27
33
  end
@@ -30,6 +36,7 @@ module Golden
30
36
 
31
37
  define_model_callbacks :build_form
32
38
 
39
+ attr_accessor :mode
33
40
  attr_reader :params, :form
34
41
 
35
42
  def initialize(params, accessors = {})
@@ -49,7 +56,7 @@ module Golden
49
56
  end
50
57
 
51
58
  def result_presenter
52
- @result_presenter ||= result_presenter_class.collect form.result
59
+ @result_presenter ||= result_presenter_class.collect query_result, record_presenter_class.name
53
60
  end
54
61
 
55
62
  private
@@ -58,6 +65,16 @@ module Golden
58
65
  params
59
66
  end
60
67
 
68
+ def query_accessors
69
+ {
70
+ mode: mode
71
+ }
72
+ end
73
+
74
+ def query_result
75
+ form.result
76
+ end
77
+
61
78
  def build_form
62
79
  form_accessors = send(:query_accessors) if private_methods.include? :query_accessors
63
80
  form_class.new query_params, form_accessors
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Golden
4
+ class QueryFormPresenter < Golden::SingleFormPresenter
5
+ delegate :result, to: :form
6
+
7
+ def query_url
8
+ raise NotImplementedError
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Golden
4
+ class QueryRecordPresenter < Golden::ApplicationPresenter
5
+ class << self
6
+ def collect(records)
7
+ ::Golden::QueryResultPresenter.collect(records, name)
8
+ end
9
+ end
10
+
11
+ def initialize(record, accessors = {})
12
+ super(accessors)
13
+ @record = record
14
+ end
15
+ end
16
+ end
@@ -2,23 +2,43 @@
2
2
 
3
3
  module Golden
4
4
  class QueryResultPresenter < Golden::ApplicationPresenter
5
- include Enumerable
5
+ class << self
6
+ def collect(result, presenter_class)
7
+ new(
8
+ records: paginated_array(result),
9
+ presenter_class: presenter_class.to_s
10
+ )
11
+ end
6
12
 
7
- attr_reader :records, :presenters
13
+ def paginated_array(result)
14
+ return result unless result.is_a?(Array)
15
+ return result unless ::Object.const_defined? 'Kaminari'
16
+ return Kaminari.paginate_array([], total_count: 0).page(1) if result.empty?
8
17
 
9
- def initialize(records, presenter_class)
10
- @records = records
11
- @presenters = records.map { |record| presenter_class.constantize.new record }
18
+ Kaminari.paginate_array(result).page(1).per(result.size)
19
+ end
12
20
  end
13
21
 
22
+ include Enumerable
23
+
24
+ attr_accessor :records, :presenter_class
25
+
14
26
  def each(&block)
15
27
  presenters.each(&block)
16
28
  end
17
29
 
30
+ def presenters
31
+ @presenters ||= records.map { |record| presenter_class.constantize.new record }
32
+ end
33
+
18
34
  def total_count
19
35
  records.total_count
20
36
  end
21
37
 
38
+ def total_pages
39
+ records.total_pages
40
+ end
41
+
22
42
  def current_page
23
43
  records.current_page
24
44
  end
@@ -26,5 +46,25 @@ module Golden
26
46
  def per_page
27
47
  records.limit_value
28
48
  end
49
+
50
+ def next_page
51
+ records.next_page
52
+ end
53
+
54
+ def prev_page
55
+ records.prev_page
56
+ end
57
+
58
+ def first_page?
59
+ records.first_page?
60
+ end
61
+
62
+ def last_page?
63
+ records.last_page?
64
+ end
65
+
66
+ def out_of_range?
67
+ records.out_of_range?
68
+ end
29
69
  end
30
70
  end
@@ -1,5 +1,5 @@
1
1
  module Golden
2
2
  module Objects
3
- VERSION = '0.1.0'
3
+ VERSION = '0.3.3'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: golden-objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tse-Ching Ho
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-26 00:00:00.000000000 Z
11
+ date: 2021-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -69,6 +69,9 @@ files:
69
69
  - bin/console
70
70
  - bin/setup
71
71
  - golden-objects.gemspec
72
+ - lib/golden/action_view/extension.rb
73
+ - lib/golden/action_view/form_builder.rb
74
+ - lib/golden/action_view/form_helper.rb
72
75
  - lib/golden/active_model_concerns.rb
73
76
  - lib/golden/active_model_concerns/datepicker_concern.rb
74
77
  - lib/golden/active_model_concerns/has_persisted_record_concern.rb
@@ -98,6 +101,8 @@ files:
98
101
  - lib/golden/objects/query/query_context.rb
99
102
  - lib/golden/objects/query/query_form.rb
100
103
  - lib/golden/objects/query/query_form_operator.rb
104
+ - lib/golden/objects/query/query_form_presenter.rb
105
+ - lib/golden/objects/query/query_record_presenter.rb
101
106
  - lib/golden/objects/query/query_result_presenter.rb
102
107
  - lib/golden/objects/version.rb
103
108
  homepage: https://github.com/goldenio/golden-objects
@@ -106,7 +111,7 @@ licenses:
106
111
  metadata:
107
112
  homepage_uri: https://github.com/goldenio/golden-objects
108
113
  source_code_uri: https://github.com/goldenio/golden-objects.git
109
- changelog_uri: https://github.com/goldenio/golden-objects/CHANGELOG.md
114
+ changelog_uri: https://github.com/goldenio/golden-objects/blob/master/CHANGELOG.md
110
115
  post_install_message:
111
116
  rdoc_options: []
112
117
  require_paths: