golden-objects 0.1.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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: