jpie 0.4.0 → 0.4.2

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.
@@ -0,0 +1,303 @@
1
+ # Pagination Example
2
+
3
+ This example demonstrates how to implement pagination with JPie resources using both simple and JSON:API standard pagination parameters.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Model (`app/models/article.rb`)
8
+ ```ruby
9
+ class Article < ActiveRecord::Base
10
+ validates :title, presence: true
11
+ validates :content, presence: true
12
+ end
13
+ ```
14
+
15
+ ### 2. Resource (`app/resources/article_resource.rb`)
16
+ ```ruby
17
+ class ArticleResource < JPie::Resource
18
+ attributes :title, :content, :published_at
19
+ end
20
+ ```
21
+
22
+ ### 3. Controller (`app/controllers/articles_controller.rb`)
23
+ ```ruby
24
+ class ArticlesController < ApplicationController
25
+ include JPie::Controller
26
+ end
27
+ ```
28
+
29
+ ### 4. Routes (`config/routes.rb`)
30
+ ```ruby
31
+ Rails.application.routes.draw do
32
+ resources :articles
33
+ end
34
+ ```
35
+
36
+ ## HTTP Examples
37
+
38
+ ### Simple Pagination Parameters
39
+
40
+ #### Get First Page with 5 Articles
41
+ ```http
42
+ GET /articles?page=1&per_page=5
43
+ Accept: application/vnd.api+json
44
+
45
+ HTTP/1.1 200 OK
46
+ Content-Type: application/vnd.api+json
47
+
48
+ {
49
+ "data": [
50
+ {
51
+ "id": "1",
52
+ "type": "articles",
53
+ "attributes": {
54
+ "title": "First Article",
55
+ "content": "Content of first article",
56
+ "published_at": "2024-01-01T10:00:00Z"
57
+ }
58
+ },
59
+ {
60
+ "id": "2",
61
+ "type": "articles",
62
+ "attributes": {
63
+ "title": "Second Article",
64
+ "content": "Content of second article",
65
+ "published_at": "2024-01-02T10:00:00Z"
66
+ }
67
+ }
68
+ ],
69
+ "meta": {
70
+ "pagination": {
71
+ "page": 1,
72
+ "per_page": 5,
73
+ "total_pages": 4,
74
+ "total_count": 20
75
+ }
76
+ },
77
+ "links": {
78
+ "self": "http://example.com/articles?page=1&per_page=5",
79
+ "first": "http://example.com/articles?page=1&per_page=5",
80
+ "last": "http://example.com/articles?page=4&per_page=5",
81
+ "next": "http://example.com/articles?page=2&per_page=5"
82
+ }
83
+ }
84
+ ```
85
+
86
+ #### Get Second Page
87
+ ```http
88
+ GET /articles?page=2&per_page=5
89
+ Accept: application/vnd.api+json
90
+
91
+ HTTP/1.1 200 OK
92
+ Content-Type: application/vnd.api+json
93
+
94
+ {
95
+ "data": [
96
+ {
97
+ "id": "6",
98
+ "type": "articles",
99
+ "attributes": {
100
+ "title": "Sixth Article",
101
+ "content": "Content of sixth article",
102
+ "published_at": "2024-01-06T10:00:00Z"
103
+ }
104
+ }
105
+ ],
106
+ "meta": {
107
+ "pagination": {
108
+ "page": 2,
109
+ "per_page": 5,
110
+ "total_pages": 4,
111
+ "total_count": 20
112
+ }
113
+ },
114
+ "links": {
115
+ "self": "http://example.com/articles?page=2&per_page=5",
116
+ "first": "http://example.com/articles?page=1&per_page=5",
117
+ "last": "http://example.com/articles?page=4&per_page=5",
118
+ "prev": "http://example.com/articles?page=1&per_page=5",
119
+ "next": "http://example.com/articles?page=3&per_page=5"
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### JSON:API Standard Pagination Parameters
125
+
126
+ #### Get First Page Using JSON:API Format
127
+ ```http
128
+ GET /articles?page[number]=1&page[size]=3
129
+ Accept: application/vnd.api+json
130
+
131
+ HTTP/1.1 200 OK
132
+ Content-Type: application/vnd.api+json
133
+
134
+ {
135
+ "data": [
136
+ {
137
+ "id": "1",
138
+ "type": "articles",
139
+ "attributes": {
140
+ "title": "First Article",
141
+ "content": "Content of first article",
142
+ "published_at": "2024-01-01T10:00:00Z"
143
+ }
144
+ },
145
+ {
146
+ "id": "2",
147
+ "type": "articles",
148
+ "attributes": {
149
+ "title": "Second Article",
150
+ "content": "Content of second article",
151
+ "published_at": "2024-01-02T10:00:00Z"
152
+ }
153
+ },
154
+ {
155
+ "id": "3",
156
+ "type": "articles",
157
+ "attributes": {
158
+ "title": "Third Article",
159
+ "content": "Content of third article",
160
+ "published_at": "2024-01-03T10:00:00Z"
161
+ }
162
+ }
163
+ ],
164
+ "meta": {
165
+ "pagination": {
166
+ "page": 1,
167
+ "per_page": 3,
168
+ "total_pages": 7,
169
+ "total_count": 20
170
+ }
171
+ },
172
+ "links": {
173
+ "self": "http://example.com/articles?page=1&per_page=3",
174
+ "first": "http://example.com/articles?page=1&per_page=3",
175
+ "last": "http://example.com/articles?page=7&per_page=3",
176
+ "next": "http://example.com/articles?page=2&per_page=3"
177
+ }
178
+ }
179
+ ```
180
+
181
+ ### Pagination with Sorting
182
+
183
+ #### Get Sorted and Paginated Results
184
+ ```http
185
+ GET /articles?sort=-published_at&page=1&per_page=3
186
+ Accept: application/vnd.api+json
187
+
188
+ HTTP/1.1 200 OK
189
+ Content-Type: application/vnd.api+json
190
+
191
+ {
192
+ "data": [
193
+ {
194
+ "id": "20",
195
+ "type": "articles",
196
+ "attributes": {
197
+ "title": "Latest Article",
198
+ "content": "Most recent content",
199
+ "published_at": "2024-01-20T10:00:00Z"
200
+ }
201
+ },
202
+ {
203
+ "id": "19",
204
+ "type": "articles",
205
+ "attributes": {
206
+ "title": "Second Latest Article",
207
+ "content": "Second most recent content",
208
+ "published_at": "2024-01-19T10:00:00Z"
209
+ }
210
+ },
211
+ {
212
+ "id": "18",
213
+ "type": "articles",
214
+ "attributes": {
215
+ "title": "Third Latest Article",
216
+ "content": "Third most recent content",
217
+ "published_at": "2024-01-18T10:00:00Z"
218
+ }
219
+ }
220
+ ],
221
+ "meta": {
222
+ "pagination": {
223
+ "page": 1,
224
+ "per_page": 3,
225
+ "total_pages": 7,
226
+ "total_count": 20
227
+ }
228
+ },
229
+ "links": {
230
+ "self": "http://example.com/articles?sort=-published_at&page=1&per_page=3",
231
+ "first": "http://example.com/articles?sort=-published_at&page=1&per_page=3",
232
+ "last": "http://example.com/articles?sort=-published_at&page=7&per_page=3",
233
+ "next": "http://example.com/articles?sort=-published_at&page=2&per_page=3"
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Last Page Response
239
+
240
+ #### Get Last Page
241
+ ```http
242
+ GET /articles?page=4&per_page=5
243
+ Accept: application/vnd.api+json
244
+
245
+ HTTP/1.1 200 OK
246
+ Content-Type: application/vnd.api+json
247
+
248
+ {
249
+ "data": [
250
+ {
251
+ "id": "20",
252
+ "type": "articles",
253
+ "attributes": {
254
+ "title": "Last Article",
255
+ "content": "Content of last article",
256
+ "published_at": "2024-01-20T10:00:00Z"
257
+ }
258
+ }
259
+ ],
260
+ "meta": {
261
+ "pagination": {
262
+ "page": 4,
263
+ "per_page": 5,
264
+ "total_pages": 4,
265
+ "total_count": 20
266
+ }
267
+ },
268
+ "links": {
269
+ "self": "http://example.com/articles?page=4&per_page=5",
270
+ "first": "http://example.com/articles?page=1&per_page=5",
271
+ "last": "http://example.com/articles?page=4&per_page=5",
272
+ "prev": "http://example.com/articles?page=3&per_page=5"
273
+ }
274
+ }
275
+ ```
276
+
277
+ ### Empty Results
278
+
279
+ #### Get Page Beyond Available Data
280
+ ```http
281
+ GET /articles?page=10&per_page=5
282
+ Accept: application/vnd.api+json
283
+
284
+ HTTP/1.1 200 OK
285
+ Content-Type: application/vnd.api+json
286
+
287
+ {
288
+ "data": [],
289
+ "meta": {
290
+ "pagination": {
291
+ "page": 10,
292
+ "per_page": 5,
293
+ "total_pages": 4,
294
+ "total_count": 20
295
+ }
296
+ },
297
+ "links": {
298
+ "self": "http://example.com/articles?page=10&per_page=5",
299
+ "first": "http://example.com/articles?page=1&per_page=5",
300
+ "last": "http://example.com/articles?page=4&per_page=5"
301
+ }
302
+ }
303
+ ```
@@ -0,0 +1,147 @@
1
+ # Resource Attribute Configuration Example
2
+
3
+ This example demonstrates all the different ways to configure resource attributes in JPie, showcasing the various configuration patterns and customization techniques available for attributes.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Model (`app/models/user.rb`)
8
+ ```ruby
9
+ class User < ActiveRecord::Base
10
+ validates :first_name, presence: true
11
+ validates :last_name, presence: true
12
+ validates :email, presence: true, uniqueness: true
13
+ validates :username, presence: true, uniqueness: true
14
+
15
+ def active?
16
+ true # Simple boolean for demonstration
17
+ end
18
+ end
19
+ ```
20
+
21
+ ### 2. Resource with All Attribute Configuration Types (`app/resources/user_resource.rb`)
22
+ ```ruby
23
+ class UserResource < JPie::Resource
24
+ # 1. Basic attributes - direct model attribute access
25
+ attributes :email, :first_name, :last_name
26
+
27
+ # 2. Attribute with custom attr mapping (maps to different model attribute)
28
+ attribute :display_name, attr: :username
29
+
30
+ # 3. Attribute with block override
31
+ attribute :status do
32
+ object.active? ? 'active' : 'inactive'
33
+ end
34
+
35
+ # 4. Attribute with custom method override
36
+ attribute :full_name
37
+
38
+ private
39
+
40
+ # Custom method for attribute override
41
+ def full_name
42
+ "#{object.first_name} #{object.last_name}".strip
43
+ end
44
+ end
45
+ ```
46
+
47
+ ### 3. Controller (`app/controllers/users_controller.rb`)
48
+ ```ruby
49
+ class UsersController < ApplicationController
50
+ include JPie::Controller
51
+ end
52
+ ```
53
+
54
+ ## HTTP Examples
55
+
56
+ ### Create User
57
+ ```http
58
+ POST /users
59
+ Content-Type: application/vnd.api+json
60
+
61
+ {
62
+ "data": {
63
+ "type": "users",
64
+ "attributes": {
65
+ "email": "john.doe@example.com",
66
+ "first_name": "John",
67
+ "last_name": "Doe"
68
+ }
69
+ }
70
+ }
71
+
72
+ HTTP/1.1 201 Created
73
+ Content-Type: application/vnd.api+json
74
+
75
+ {
76
+ "data": {
77
+ "id": "1",
78
+ "type": "users",
79
+ "attributes": {
80
+ "email": "john.doe@example.com",
81
+ "first_name": "John",
82
+ "last_name": "Doe",
83
+ "display_name": "johndoe",
84
+ "status": "active",
85
+ "full_name": "John Doe"
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Update User
92
+ ```http
93
+ PATCH /users/1
94
+ Content-Type: application/vnd.api+json
95
+
96
+ {
97
+ "data": {
98
+ "id": "1",
99
+ "type": "users",
100
+ "attributes": {
101
+ "first_name": "Jonathan"
102
+ }
103
+ }
104
+ }
105
+
106
+ HTTP/1.1 200 OK
107
+ Content-Type: application/vnd.api+json
108
+
109
+ {
110
+ "data": {
111
+ "id": "1",
112
+ "type": "users",
113
+ "attributes": {
114
+ "email": "john.doe@example.com",
115
+ "first_name": "Jonathan",
116
+ "last_name": "Doe",
117
+ "display_name": "johndoe",
118
+ "status": "active",
119
+ "full_name": "Jonathan Doe"
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Get User with All Attribute Configuration Types
126
+ ```http
127
+ GET /users/1
128
+ Accept: application/vnd.api+json
129
+
130
+ HTTP/1.1 200 OK
131
+ Content-Type: application/vnd.api+json
132
+
133
+ {
134
+ "data": {
135
+ "id": "1",
136
+ "type": "users",
137
+ "attributes": {
138
+ "email": "john.doe@example.com",
139
+ "first_name": "John",
140
+ "last_name": "Doe",
141
+ "display_name": "johndoe",
142
+ "status": "active",
143
+ "full_name": "John Doe"
144
+ }
145
+ }
146
+ }
147
+ ```
@@ -0,0 +1,244 @@
1
+ # Meta Field Configuration Example
2
+
3
+ This example demonstrates all the different ways to define and configure meta fields in JPie, focusing on the various configuration patterns and syntax options available.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Article model
8
+ ```ruby
9
+ class Article < ActiveRecord::Base
10
+ validates :title, presence: true
11
+ validates :content, presence: true
12
+ validates :status, inclusion: { in: %w[draft published archived] }
13
+
14
+ belongs_to :author, class_name: 'User'
15
+
16
+ def word_count
17
+ content.split.length
18
+ end
19
+
20
+ def reading_time_minutes
21
+ (word_count / 200.0).ceil
22
+ end
23
+ end
24
+ ```
25
+
26
+ ### 2. Resource with All Meta Field Configuration Types
27
+ ```ruby
28
+ class ArticleResource < JPie::Resource
29
+ # 1. Basic meta attributes - direct model access and custom methods
30
+ meta_attributes :created_at, :updated_at
31
+ meta_attribute :reading_time
32
+
33
+ # 2. Meta attribute with attr mapping (maps to different model attribute)
34
+ meta_attribute :author_name, attr: :author_email
35
+
36
+ # 3. Meta attribute with block (legacy style)
37
+ meta_attribute :word_count do
38
+ object.word_count
39
+ end
40
+
41
+ # 4. Meta attribute with proc block (alternative legacy style)
42
+ meta_attribute :character_count, block: proc { object.content.length }
43
+
44
+ # 5. Short alias syntax (modern style)
45
+ meta :api_version
46
+ metas :request_id, :cache_key
47
+
48
+ # 6. Custom meta method for dynamic metadata
49
+ def meta
50
+ # Start with declared meta attributes
51
+ base_meta = super
52
+
53
+ # Add dynamic metadata
54
+ dynamic_meta = {
55
+ timestamp: Time.current.iso8601,
56
+ resource_version: '2.1'
57
+ }
58
+
59
+ # Conditional metadata based on context
60
+ if context[:include_debug]
61
+ dynamic_meta[:debug_info] = {
62
+ object_class: object.class.name,
63
+ context_keys: context.keys
64
+ }
65
+ end
66
+
67
+ # Merge and return
68
+ base_meta.merge(dynamic_meta)
69
+ end
70
+
71
+ private
72
+
73
+ # Custom method for reading_time meta attribute
74
+ def reading_time
75
+ {
76
+ minutes: object.reading_time_minutes,
77
+ formatted: "#{object.reading_time_minutes} min read"
78
+ }
79
+ end
80
+
81
+ # Meta attribute accessing context
82
+ def user_role
83
+ context[:current_user]&.role || 'anonymous'
84
+ end
85
+
86
+ # Meta attribute with conditional logic
87
+ def edit_permissions
88
+ current_user = context[:current_user]
89
+ return false unless current_user
90
+
91
+ current_user.admin? || current_user == object.author
92
+ end
93
+
94
+ # Meta attributes using short alias syntax
95
+ def api_version
96
+ '1.0'
97
+ end
98
+
99
+ def request_id
100
+ context[:request_id] || SecureRandom.uuid
101
+ end
102
+
103
+ def cache_key
104
+ "article:#{object.id}:#{object.updated_at.to_i}"
105
+ end
106
+ end
107
+ ```
108
+
109
+ ## HTTP Examples
110
+
111
+ ### Create Article
112
+ ```http
113
+ POST /articles
114
+ Content-Type: application/vnd.api+json
115
+ Authorization: Bearer user_token
116
+
117
+ {
118
+ "data": {
119
+ "type": "articles",
120
+ "attributes": {
121
+ "title": "Meta Field Configuration Guide",
122
+ "content": "This guide demonstrates all the different ways to configure meta fields in JPie...",
123
+ "status": "draft"
124
+ }
125
+ }
126
+ }
127
+
128
+ HTTP/1.1 201 Created
129
+ Content-Type: application/vnd.api+json
130
+
131
+ {
132
+ "data": {
133
+ "id": "1",
134
+ "type": "articles",
135
+ "attributes": {
136
+ "title": "Meta Field Configuration Guide",
137
+ "content": "This guide demonstrates all the different ways to configure meta fields in JPie...",
138
+ "status": "draft"
139
+ },
140
+ "meta": {
141
+ "created_at": "2024-01-15T10:30:00Z",
142
+ "updated_at": "2024-01-15T10:30:00Z",
143
+ "reading_time": {
144
+ "minutes": 3,
145
+ "formatted": "3 min read"
146
+ },
147
+ "author_name": "john@example.com",
148
+ "word_count": 450,
149
+ "character_count": 2700,
150
+ "api_version": "1.0",
151
+ "request_id": "req_abc123def456",
152
+ "cache_key": "article:1:1705492800",
153
+ "timestamp": "2024-03-15T16:45:30Z",
154
+ "resource_version": "2.1"
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Update Article
161
+ ```http
162
+ PATCH /articles/1
163
+ Content-Type: application/vnd.api+json
164
+ Authorization: Bearer user_token
165
+
166
+ {
167
+ "data": {
168
+ "id": "1",
169
+ "type": "articles",
170
+ "attributes": {
171
+ "status": "published"
172
+ }
173
+ }
174
+ }
175
+
176
+ HTTP/1.1 200 OK
177
+ Content-Type: application/vnd.api+json
178
+
179
+ {
180
+ "data": {
181
+ "id": "1",
182
+ "type": "articles",
183
+ "attributes": {
184
+ "title": "Meta Field Configuration Guide",
185
+ "content": "This guide demonstrates all the different ways to configure meta fields in JPie...",
186
+ "status": "published"
187
+ },
188
+ "meta": {
189
+ "created_at": "2024-01-15T10:30:00Z",
190
+ "updated_at": "2024-01-16T14:20:00Z",
191
+ "reading_time": {
192
+ "minutes": 3,
193
+ "formatted": "3 min read"
194
+ },
195
+ "author_name": "john@example.com",
196
+ "word_count": 450,
197
+ "character_count": 2700,
198
+ "api_version": "1.0",
199
+ "request_id": "req_xyz789ghi012",
200
+ "cache_key": "article:1:1705492800",
201
+ "timestamp": "2024-03-16T14:20:00Z",
202
+ "resource_version": "2.1"
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Get Article with All Meta Field Configurations
209
+ ```http
210
+ GET /articles/1
211
+ Accept: application/vnd.api+json
212
+ Authorization: Bearer user_token
213
+
214
+ HTTP/1.1 200 OK
215
+ Content-Type: application/vnd.api+json
216
+
217
+ {
218
+ "data": {
219
+ "id": "1",
220
+ "type": "articles",
221
+ "attributes": {
222
+ "title": "Meta Field Configuration Guide",
223
+ "content": "This guide demonstrates all the different ways to configure meta fields in JPie...",
224
+ "status": "published"
225
+ },
226
+ "meta": {
227
+ "created_at": "2024-01-15T10:30:00Z",
228
+ "updated_at": "2024-01-16T14:20:00Z",
229
+ "reading_time": {
230
+ "minutes": 3,
231
+ "formatted": "3 min read"
232
+ },
233
+ "author_name": "john@example.com",
234
+ "word_count": 450,
235
+ "character_count": 2700,
236
+ "api_version": "1.0",
237
+ "request_id": "req_abc123def456",
238
+ "cache_key": "article:1:1705492800",
239
+ "timestamp": "2024-03-16T14:20:00Z",
240
+ "resource_version": "2.1"
241
+ }
242
+ }
243
+ }
244
+ ```