api_presenter 0.2.3 → 0.2.4

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
  SHA1:
3
- metadata.gz: 73dd421a335a6bd28bb1fa83bcf1142f3f72de40
4
- data.tar.gz: 8e07b111ea8ab9d0f5dbb13420113ed9d7204825
3
+ metadata.gz: 2a3e0a34d5d50f902760195ffd58a409d3231a15
4
+ data.tar.gz: 8a4261472f7ba030c7ff72e2bed5d9c3171b98d6
5
5
  SHA512:
6
- metadata.gz: f59b72c776aa0279c438cb20744d39074e9d18de3eadf9f84a7aa9e6849fe418a99e00ea8f7edd796fe3cc5c7227e34bae678e73fa1fd8917ece3abfc4373468
7
- data.tar.gz: 36c674a4aa24f8f8af48c8ed2daba2c9e6c1dc1864b25edc23175ee4fa705508a71078775a7f2ea19784a649b82e8c458f489d4a3546a8d486b2956f85fea60a
6
+ metadata.gz: 3e3491e2c26ac8affb10ba11eaeef277be47b4b9666f205a6c9f5a07da5178b204ffcc28961e10df10811abb6a98ff5cc0e0968e0d213911f07f96d1094a94fa
7
+ data.tar.gz: e426cce2a716fc7cdcb4e5066cd9b9a51509a5c339886031c27159fe0a6698c07f08f107f939cd6c0a071637bd7f4ee942f61e2876d139757f42383e157ed367
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.2.4 (2016-11-07)
2
+
3
+ Logical restructure. No API changes.
4
+
1
5
  ## 0.2.0 (2016-11-07)
2
6
 
3
7
  Fixes require statements. Note to self: don't publish a gem right before leaving for holidays.
data/lib/api_presenter.rb CHANGED
@@ -1,205 +1,10 @@
1
1
  require 'pundit'
2
+ module ApiPresenter; end
2
3
 
3
- module ApiPresenter
4
- class Base
5
-
6
- attr_reader :current_user, :params, :relation
7
-
8
- # @example
9
- # @presenter = PostPresenter.call(
10
- # current_user: current_user,
11
- # relation: relation,
12
- # params: params
13
- # )
14
- #
15
- # @param (see #initialize)
16
- #
17
- # @return [ApiPresenter::Base]
18
- #
19
- def self.call(**kwargs)
20
- new(kwargs).call
21
- end
22
-
23
- # @param current_user [User] Optional. current_user context.
24
- # @param relation [ActiveRecord::Relation, Array] Relation or array-wrapped record(s) to present
25
- # @param params [Hash] Controller params
26
- # @option params [Boolean] :count Optional. If true, return count only.
27
- # @option params [String, Array] :include Optional. Associated resources to include.
28
- # @option params [Boolean] :policies Optional. If true, resolve polciies for relation.
29
- #
30
- def initialize(current_user: nil, relation:, params: {})
31
- @current_user = current_user
32
- @relation = relation
33
- @params = params
34
- end
35
-
36
- # @return [ApiPresenter::Base]
37
- #
38
- def call
39
- return self if count_only?
40
- initialize_resolvers
41
- call_resolvers
42
- self
43
- end
44
-
45
- # Primary collection, empty if count requested
46
- #
47
- # @return [ActiveRecord::Relation, Array<ActiveRecord::Base>]
48
- #
49
- def collection
50
- count_only? ? [] : relation
51
- end
52
-
53
- # Count of primary collection
54
- #
55
- # @note Delegate to Kaminari's `total_count` property, or regular count if not a paginated relation
56
- #
57
- # @return [Integer]
58
- #
59
- def total_count
60
- relation.respond_to?(:total_count) ? relation.total_count : relation.count
61
- end
62
-
63
- # Policies for the primary collection
64
- #
65
- # @example
66
- # [
67
- # { post_id: 1, update: true, destroy: true },
68
- # { post_id: 2, update: false, destroy: false }
69
- # ]
70
- #
71
- # @return [<Array<Hash>]
72
- #
73
- def policies
74
- @policies_resolver ? @policies_resolver.resolved_policies : {}
75
- end
76
-
77
- # Class names of included collections
78
- #
79
- # @example
80
- # [:categories, :sub_categories, :users]
81
- #
82
- # @return [Array<Symbol>]
83
- #
84
- def included_collection_names
85
- @included_collection_names ||= ParseIncludeParams.call(params[:include])
86
- end
87
-
88
- # Map of included collection names and loaded record
89
- #
90
- # @example
91
- # {
92
- # categories: [#<Category id:1>],
93
- # sub_categories: [#<SubCategory id:1>],
94
- # users: [#<User id:1>, #<User id:2]
95
- # }
96
- #
97
- # @return [Hash]
98
- #
99
- def included_collections
100
- @included_collections_resolver ? @included_collections_resolver.resolved_collections : {}
101
- end
102
-
103
- # Preload additional records with the relation
104
- #
105
- # @note Called by resolvers, but can also be called if additional data is required that does
106
- # not need to be loaded as an included collection, and for some reason cannot be chained
107
- # onto the original relation.
108
- #
109
- # @param associations [Symbol, Array<Symbol>]
110
- #
111
- def preload(associations)
112
- @relation = @relation.preload(associations)
113
- end
114
-
115
- # Hash map that defines the sources for included collection names
116
- #
117
- # @example
118
- # def associations_map
119
- # {
120
- # categories: { associations: { sub_category: :category } },
121
- # sub_categories: { associations: :sub_category },
122
- # users: { associations: [:creator, :publisher] }
123
- # }
124
- # end
125
- #
126
- # @abstract
127
- #
128
- # @return [Hash]
129
- #
130
- def associations_map
131
- {}
132
- end
133
-
134
- # Policy methods to resolve for the primary relation
135
- #
136
- # @example Single
137
- # def policy_methods
138
- # :update
139
- # end
140
- #
141
- # @example Multiple
142
- # def policy_methods
143
- # [:update, :destroy]
144
- # end
145
- #
146
- # @abstract
147
- #
148
- # @return [Symbol, Array<Symbol>]
149
- #
150
- def policy_methods
151
- []
152
- end
153
-
154
- # Policy associations to preload to optimize policy resolution
155
- #
156
- # @example Single
157
- # def policy_associations
158
- # :user_profile
159
- # end
160
- #
161
- # @example Multiple
162
- # def policy_associations
163
- # [:user_profile, :company]
164
- # end
165
- #
166
- # @abstract
167
- #
168
- # @return [Symbol, Array<Symbol>]
169
- #
170
- def policy_associations
171
- []
172
- end
173
-
174
- private
175
-
176
- def count_only?
177
- @count_only ||= !!params[:count]
178
- end
179
-
180
- def resolve_policies?
181
- @resolve_policies ||= current_user && !!params[:policies]
182
- end
183
-
184
- def resolve_included_collctions?
185
- included_collection_names.any?
186
- end
187
-
188
- def initialize_resolvers
189
- @policies_resolver = Resolvers::PoliciesResolver.new(self) if resolve_policies?
190
- @included_collections_resolver = Resolvers::IncludedCollectionsResolver.new(self) if resolve_included_collctions?
191
- end
192
-
193
- def call_resolvers
194
- @policies_resolver.call if @policies_resolver
195
- @included_collections_resolver.call if @included_collections_resolver
196
- end
197
- end
198
- end
199
-
200
- require 'api_presenter/parse_include_params'
201
- require 'api_presenter/version'
4
+ require 'api_presenter/base'
202
5
  require 'api_presenter/concerns/presentable'
6
+ require 'api_presenter/parsers/parse_include_params'
203
7
  require 'api_presenter/resolvers/base'
204
- require 'api_presenter/resolvers/policies_resolver'
205
8
  require 'api_presenter/resolvers/included_collections_resolver'
9
+ require 'api_presenter/resolvers/policies_resolver'
10
+ require 'api_presenter/version'
@@ -0,0 +1,196 @@
1
+ module ApiPresenter
2
+ class Base
3
+
4
+ attr_reader :current_user, :params, :relation
5
+
6
+ # @example
7
+ # @presenter = PostPresenter.call(
8
+ # current_user: current_user,
9
+ # relation: relation,
10
+ # params: params
11
+ # )
12
+ #
13
+ # @param (see #initialize)
14
+ #
15
+ # @return [ApiPresenter::Base]
16
+ #
17
+ def self.call(**kwargs)
18
+ new(kwargs).call
19
+ end
20
+
21
+ # @param current_user [User] Optional. current_user context.
22
+ # @param relation [ActiveRecord::Relation, Array] Relation or array-wrapped record(s) to present
23
+ # @param params [Hash] Controller params
24
+ # @option params [Boolean] :count Optional. If true, return count only.
25
+ # @option params [String, Array] :include Optional. Associated resources to include.
26
+ # @option params [Boolean] :policies Optional. If true, resolve polciies for relation.
27
+ #
28
+ def initialize(current_user: nil, relation:, params: {})
29
+ @current_user = current_user
30
+ @relation = relation
31
+ @params = params
32
+ end
33
+
34
+ # @return [ApiPresenter::Base]
35
+ #
36
+ def call
37
+ return self if count_only?
38
+ initialize_resolvers
39
+ call_resolvers
40
+ self
41
+ end
42
+
43
+ # Primary collection, empty if count requested
44
+ #
45
+ # @return [ActiveRecord::Relation, Array<ActiveRecord::Base>]
46
+ #
47
+ def collection
48
+ count_only? ? [] : relation
49
+ end
50
+
51
+ # Count of primary collection
52
+ #
53
+ # @note Delegate to Kaminari's `total_count` property, or regular count if not a paginated relation
54
+ #
55
+ # @return [Integer]
56
+ #
57
+ def total_count
58
+ relation.respond_to?(:total_count) ? relation.total_count : relation.count
59
+ end
60
+
61
+ # Policies for the primary collection
62
+ #
63
+ # @example
64
+ # [
65
+ # { post_id: 1, update: true, destroy: true },
66
+ # { post_id: 2, update: false, destroy: false }
67
+ # ]
68
+ #
69
+ # @return [<Array<Hash>]
70
+ #
71
+ def policies
72
+ @policies_resolver ? @policies_resolver.resolved_policies : {}
73
+ end
74
+
75
+ # Class names of included collections
76
+ #
77
+ # @example
78
+ # [:categories, :sub_categories, :users]
79
+ #
80
+ # @return [Array<Symbol>]
81
+ #
82
+ def included_collection_names
83
+ @included_collection_names ||= Parsers::ParseIncludeParams.call(params[:include])
84
+ end
85
+
86
+ # Map of included collection names and loaded record
87
+ #
88
+ # @example
89
+ # {
90
+ # categories: [#<Category id:1>],
91
+ # sub_categories: [#<SubCategory id:1>],
92
+ # users: [#<User id:1>, #<User id:2]
93
+ # }
94
+ #
95
+ # @return [Hash]
96
+ #
97
+ def included_collections
98
+ @included_collections_resolver ? @included_collections_resolver.resolved_collections : {}
99
+ end
100
+
101
+ # Preload additional records with the relation
102
+ #
103
+ # @note Called by resolvers, but can also be called if additional data is required that does
104
+ # not need to be loaded as an included collection, and for some reason cannot be chained
105
+ # onto the original relation.
106
+ #
107
+ # @param associations [Symbol, Array<Symbol>]
108
+ #
109
+ def preload(associations)
110
+ @relation = @relation.preload(associations)
111
+ end
112
+
113
+ # Hash map that defines the sources for included collection names
114
+ #
115
+ # @example
116
+ # def associations_map
117
+ # {
118
+ # categories: { associations: { sub_category: :category } },
119
+ # sub_categories: { associations: :sub_category },
120
+ # users: { associations: [:creator, :publisher] }
121
+ # }
122
+ # end
123
+ #
124
+ # @abstract
125
+ #
126
+ # @return [Hash]
127
+ #
128
+ def associations_map
129
+ {}
130
+ end
131
+
132
+ # Policy methods to resolve for the primary relation
133
+ #
134
+ # @example Single
135
+ # def policy_methods
136
+ # :update
137
+ # end
138
+ #
139
+ # @example Multiple
140
+ # def policy_methods
141
+ # [:update, :destroy]
142
+ # end
143
+ #
144
+ # @abstract
145
+ #
146
+ # @return [Symbol, Array<Symbol>]
147
+ #
148
+ def policy_methods
149
+ []
150
+ end
151
+
152
+ # Policy associations to preload to optimize policy resolution
153
+ #
154
+ # @example Single
155
+ # def policy_associations
156
+ # :user_profile
157
+ # end
158
+ #
159
+ # @example Multiple
160
+ # def policy_associations
161
+ # [:user_profile, :company]
162
+ # end
163
+ #
164
+ # @abstract
165
+ #
166
+ # @return [Symbol, Array<Symbol>]
167
+ #
168
+ def policy_associations
169
+ []
170
+ end
171
+
172
+ private
173
+
174
+ def count_only?
175
+ @count_only ||= !!params[:count]
176
+ end
177
+
178
+ def resolve_policies?
179
+ @resolve_policies ||= current_user && !!params[:policies]
180
+ end
181
+
182
+ def resolve_included_collctions?
183
+ included_collection_names.any?
184
+ end
185
+
186
+ def initialize_resolvers
187
+ @policies_resolver = Resolvers::PoliciesResolver.new(self) if resolve_policies?
188
+ @included_collections_resolver = Resolvers::IncludedCollectionsResolver.new(self) if resolve_included_collctions?
189
+ end
190
+
191
+ def call_resolvers
192
+ @policies_resolver.call if @policies_resolver
193
+ @included_collections_resolver.call if @included_collections_resolver
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,27 @@
1
+ module ApiPresenter
2
+ module Parsers
3
+
4
+ # Parses values into array of acceptable association map keys:
5
+ # * Removes blanks and dups
6
+ # * Underscores camel-cased keys
7
+ # * Converts to symbol
8
+ #
9
+ # @param values [String, Array<String>, Array<Symbol>] Comma-delimited string or array
10
+ #
11
+ # @return [Array<Symbol>]
12
+ #
13
+ class ParseIncludeParams
14
+ def self.call(values)
15
+ return [] if values.blank?
16
+
17
+ array = values.is_a?(Array) ? values.dup : values.split(',')
18
+ array.select!(&:present?)
19
+ array.map! { |value| value.try(:underscore) || value }
20
+ array.uniq!
21
+ array.map!(&:to_sym)
22
+
23
+ array
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module ApiPresenter
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuval Kordov
@@ -129,8 +129,9 @@ files:
129
129
  - bin/console
130
130
  - bin/setup
131
131
  - lib/api_presenter.rb
132
+ - lib/api_presenter/base.rb
132
133
  - lib/api_presenter/concerns/presentable.rb
133
- - lib/api_presenter/parse_include_params.rb
134
+ - lib/api_presenter/parsers/parse_include_params.rb
134
135
  - lib/api_presenter/resolvers/base.rb
135
136
  - lib/api_presenter/resolvers/included_collections_resolver.rb
136
137
  - lib/api_presenter/resolvers/policies_resolver.rb
@@ -1,25 +0,0 @@
1
- module ApiPresenter
2
-
3
- # Parses values into array of acceptable association map keys:
4
- # * Removes blanks and dups
5
- # * Underscores camel-cased keys
6
- # * Converts to symbol
7
- #
8
- # @param values [String, Array<String>, Array<Symbol>] Comma-delimited string or array
9
- #
10
- # @return [Array<Symbol>]
11
- #
12
- class ParseIncludeParams
13
- def self.call(values)
14
- return [] if values.blank?
15
-
16
- array = values.is_a?(Array) ? values.dup : values.split(',')
17
- array.select!(&:present?)
18
- array.map! { |value| value.try(:underscore) || value }
19
- array.uniq!
20
- array.map!(&:to_sym)
21
-
22
- array
23
- end
24
- end
25
- end