agris 0.18.0 → 1.0.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 +4 -4
- data/.claude/agents/app-explorer.md +212 -0
- data/.claude/agents/cross-review.md +152 -0
- data/.claude/agents/developer.md +215 -0
- data/.claude/agents/pm.md +401 -0
- data/.claude/agents/qa.md +235 -0
- data/.claude/agents/reviewer.md +185 -0
- data/.claude/agents/spec-preflight.md +167 -0
- data/.claude/agents/spec.md +244 -0
- data/.claude/ai/agent-rules.md +36 -0
- data/.claude/ai/spec-writing-guide.md +43 -0
- data/.claude/commands/app-explorer.md +87 -0
- data/.claude/commands/cross-review.md +91 -0
- data/.claude/commands/developer.md +118 -0
- data/.claude/commands/pm.md +166 -0
- data/.claude/commands/qa.md +111 -0
- data/.claude/commands/reviewer.md +86 -0
- data/.claude/commands/spec.md +172 -0
- data/.github/workflows/ci.yml +62 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +10 -103
- data/.ruby-version +1 -0
- data/CHANGELOG.md +79 -0
- data/CLAUDE.md +98 -0
- data/README.md +9 -1
- data/agris.gemspec +3 -2
- data/docs/RELEASING.md +147 -0
- data/docs/specs/README.md +26 -0
- data/docs/specs/accounts-payables/.gitkeep +0 -0
- data/docs/specs/accounts-receivables/.gitkeep +0 -0
- data/docs/specs/grain/.gitkeep +0 -0
- data/docs/specs/inventory/.gitkeep +0 -0
- data/docs/specs/messages/.gitkeep +0 -0
- data/docs/specs/rename-folder.example.md +82 -0
- data/docs/specs/spec-template.md +68 -0
- data/docs/specs/support/.gitkeep +0 -0
- data/lib/agris/api/accounts_payables/disbursements.rb +19 -0
- data/lib/agris/api/accounts_payables/new_disbursement.rb +42 -0
- data/lib/agris/api/accounts_payables.rb +3 -0
- data/lib/agris/api/accounts_receivables/invoices.rb +1 -1
- data/lib/agris/api/accounts_receivables/new_payment.rb +96 -0
- data/lib/agris/api/accounts_receivables/new_payment_remark.rb +38 -0
- data/lib/agris/api/accounts_receivables/payment_post_result.rb +35 -0
- data/lib/agris/api/accounts_receivables/payments.rb +33 -0
- data/lib/agris/api/accounts_receivables.rb +6 -0
- data/lib/agris/api/document_query_response.rb +6 -2
- data/lib/agris/api/grain/contract.rb +3 -3
- data/lib/agris/api/grain/new_ticket.rb +1 -0
- data/lib/agris/api/grain/purchase_contracts.rb +1 -1
- data/lib/agris/api/grain/sales_contracts.rb +1 -1
- data/lib/agris/api/grain.rb +4 -2
- data/lib/agris/api/inventory/delivery_tickets.rb +1 -1
- data/lib/agris/api/inventory/orders.rb +1 -1
- data/lib/agris/api/messages/changed_query_base.rb +1 -0
- data/lib/agris/api/messages/document_query_base.rb +1 -0
- data/lib/agris/api/messages/import.rb +1 -0
- data/lib/agris/api/name/new_farm_field.rb +5 -0
- data/lib/agris/api/new_voucher.rb +1 -3
- data/lib/agris/api/post_result.rb +9 -1
- data/lib/agris/client.rb +3 -1
- data/lib/agris/version.rb +1 -1
- data/lib/agris.rb +1 -0
- data/lib/hash.rb +1 -0
- metadata +48 -13
- data/.circleci/config.yml +0 -45
- data/.codeclimate.yml +0 -5
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# /spec
|
|
2
|
+
|
|
3
|
+
Generate and maintain implementation-ready specs from Jira work items or developer descriptions.
|
|
4
|
+
|
|
5
|
+
**Agent:** `~/.config/claude-playbook/global/agents/spec.md`
|
|
6
|
+
|
|
7
|
+
## Mode Detection
|
|
8
|
+
|
|
9
|
+
**Pull mode (default):** Ask ONE question at a time with numbered options. Never ask two questions in one message.
|
|
10
|
+
|
|
11
|
+
**Push mode:** If `$ARGUMENTS` contains `--push`, or if both `issue` AND `spec` arguments are provided, skip questions and execute directly using the full protocol below.
|
|
12
|
+
|
|
13
|
+
## Arguments
|
|
14
|
+
| Key | Required | Description | Example |
|
|
15
|
+
|-----|----------|-------------|---------|
|
|
16
|
+
| issue | No | Jira issue key to generate spec from | `issue=PROJ-42` |
|
|
17
|
+
| spec | No | Path to existing spec to update | `spec=docs/specs/SPEC-003-auth.md` |
|
|
18
|
+
| --push | No | Skip pull prompting, execute directly | `--push` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Pull Prompting Flow
|
|
23
|
+
|
|
24
|
+
### Step 1 — Intent
|
|
25
|
+
|
|
26
|
+
If no arguments or only partial arguments are provided, start here:
|
|
27
|
+
|
|
28
|
+
> **What would you like to do?**
|
|
29
|
+
>
|
|
30
|
+
> 1. Generate a spec from a Jira issue (epic/story/subtask)
|
|
31
|
+
> 2. Update an existing spec with changes from Jira
|
|
32
|
+
> 3. Generate a spec from scratch (no Jira issue)
|
|
33
|
+
> 4. Review/audit an existing spec
|
|
34
|
+
|
|
35
|
+
Wait for the user's answer before proceeding.
|
|
36
|
+
|
|
37
|
+
### Step 2 — Source Material
|
|
38
|
+
|
|
39
|
+
Based on the answer, ask ONE follow-up:
|
|
40
|
+
|
|
41
|
+
- **If option 1:** "What is the Jira issue key? (e.g., PROJ-42)"
|
|
42
|
+
Then execute the Jira Hierarchy Crawl: fetch the issue, crawl upward (subtask → story → epic), follow `blocks`/`is blocked by` links, pull sibling names if context is thin, search Confluence for related BRDs/PRDs. Summarize what you found. Ask: "Does this capture the scope? (1) Yes, proceed (2) No, let me clarify"
|
|
43
|
+
- **If option 2:** "Which spec?" List existing specs in `docs/specs/` as numbered options.
|
|
44
|
+
Then fetch current Jira state for linked issues and diff against the spec.
|
|
45
|
+
- **If option 3:** "Describe the feature in 1-2 sentences. Who is the user and what problem does it solve?"
|
|
46
|
+
- **If option 4:** "Which spec?" List existing specs as numbered options.
|
|
47
|
+
|
|
48
|
+
### Step 3 — Scope Confirmation
|
|
49
|
+
|
|
50
|
+
After gathering source material, present a scope proposal:
|
|
51
|
+
|
|
52
|
+
> **Based on what I found, I think the spec should cover:**
|
|
53
|
+
> - Feature: {name}
|
|
54
|
+
> - Scope level: {epic / story / subset}
|
|
55
|
+
> - In scope: {1-3 bullet summary}
|
|
56
|
+
> - Out of scope: {what this does NOT cover}
|
|
57
|
+
> - Related specs: {any existing specs that overlap}
|
|
58
|
+
>
|
|
59
|
+
> **Does this scope look right?**
|
|
60
|
+
> 1. Yes, write the spec
|
|
61
|
+
> 2. Add something
|
|
62
|
+
> 3. Remove something
|
|
63
|
+
> 4. Start over
|
|
64
|
+
|
|
65
|
+
### Step 4 — Infrastructure Check
|
|
66
|
+
|
|
67
|
+
If the scope includes Docker, Terraform, CI/CD, or deployment:
|
|
68
|
+
|
|
69
|
+
> **This feature includes infrastructure components. Recommendation:**
|
|
70
|
+
> 1. Split into separate app spec + infra spec (recommended)
|
|
71
|
+
> 2. Keep as one spec
|
|
72
|
+
> 3. Spec only the app portion, flag infra for DevOps
|
|
73
|
+
|
|
74
|
+
### Step 5 — Execute
|
|
75
|
+
|
|
76
|
+
Proceed to the Protocol below.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Protocol
|
|
81
|
+
|
|
82
|
+
### Standards — Read When Relevant
|
|
83
|
+
|
|
84
|
+
Read these files when your spec work touches each area:
|
|
85
|
+
|
|
86
|
+
| When writing about | Read this file |
|
|
87
|
+
|-------------------|---------------|
|
|
88
|
+
| Naming conventions | `~/.config/claude-playbook/global/standards/practices/naming.md` |
|
|
89
|
+
| Spec format | `~/.config/claude-playbook/global/templates/ai/spec-writing-guide.md` (scaffolded to `.claude/ai/` in projects) |
|
|
90
|
+
| Architecture or layering | `~/.config/claude-playbook/global/standards/practices/architecture.md` |
|
|
91
|
+
| Testing strategy | `~/.config/claude-playbook/global/standards/practices/testing.md` |
|
|
92
|
+
|
|
93
|
+
### Jira Hierarchy Crawl
|
|
94
|
+
|
|
95
|
+
When generating from a Jira issue:
|
|
96
|
+
1. Fetch the given issue.
|
|
97
|
+
2. Crawl upward: subtask → parent story → parent epic (as far as the chain goes).
|
|
98
|
+
3. Follow `blocks` and `is blocked by` links (not `relates to`).
|
|
99
|
+
4. If story/epic description is thin, pull sibling names and summaries for scope context.
|
|
100
|
+
5. Search Confluence for BRDs/PRDs linked from the epic or matching keywords.
|
|
101
|
+
|
|
102
|
+
### Existing Spec Check
|
|
103
|
+
|
|
104
|
+
Before writing any spec:
|
|
105
|
+
1. Scan `docs/specs/` for specs referencing the same Jira issue keys, epic, or feature area.
|
|
106
|
+
2. If a matching spec exists: switch to update flow.
|
|
107
|
+
3. If related specs exist: present to developer for cross-referencing.
|
|
108
|
+
|
|
109
|
+
### Spec Authoring
|
|
110
|
+
|
|
111
|
+
1. **Read the spec template** at `~/.config/claude-playbook/global/templates/specs/spec-template.md` — do not rely on memory.
|
|
112
|
+
2. Scan `docs/specs/` for existing specs. Determine next SPEC ID number.
|
|
113
|
+
3. Write the spec in `docs/specs/SPEC-{###}-{kebab-title}.md`.
|
|
114
|
+
4. Include: numbered rules (R#), acceptance criteria (AC-#), acceptance tests (AT#) in Given/When/Then, edge cases (E#).
|
|
115
|
+
5. Every AT must include `Covers: R#` references.
|
|
116
|
+
6. Include all sections that apply, omit sections that genuinely don't (no "N/A" filler).
|
|
117
|
+
7. Keep spec under 500 lines. If larger, propose decomposition.
|
|
118
|
+
8. Present draft to developer for approval before writing to disk.
|
|
119
|
+
|
|
120
|
+
### Task Breakdown
|
|
121
|
+
|
|
122
|
+
1. Propose tasks from spec ACs, sized for small PRs (1-3 points).
|
|
123
|
+
2. Estimate Fibonacci complexity per task (1, 2, 3, 5, 8, 13). These are preliminary — PM may adjust.
|
|
124
|
+
3. Split tasks >= 5 points or document rationale for keeping whole.
|
|
125
|
+
4. Output tasks in Jira-ready format (operational mode) or track in spec (GitHub-only mode).
|
|
126
|
+
5. Verify all ACs are covered by at least one proposed task.
|
|
127
|
+
|
|
128
|
+
The spec agent does NOT create Jira issues. The PM or developer creates them from the proposed breakdown.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Self-Test Checklist
|
|
133
|
+
|
|
134
|
+
Before handoff, verify ALL of these. Output the checklist with pass/fail:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
SELF-TEST
|
|
138
|
+
- [ ] Every rule has a unique R# ID
|
|
139
|
+
- [ ] Every AC has a unique AC-# ID
|
|
140
|
+
- [ ] Every AT has a unique AT# ID and Covers: R# reference
|
|
141
|
+
- [ ] No ambiguous language (should probably, generally, maybe)
|
|
142
|
+
- [ ] No duplicate IDs
|
|
143
|
+
- [ ] Every R# is covered by at least one AT#
|
|
144
|
+
- [ ] Every AC is mapped to at least one proposed task
|
|
145
|
+
- [ ] Tasks >= 5 points have split review or documented rationale
|
|
146
|
+
- [ ] Out-of-scope section is explicit
|
|
147
|
+
- [ ] Cross-referenced specs listed in Dependencies
|
|
148
|
+
Standards consulted: [{list of practice files actually read}]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Handoff (MANDATORY)
|
|
152
|
+
|
|
153
|
+
Output a SPEC_TO_DEV handoff summary:
|
|
154
|
+
- Spec path and SPEC ID
|
|
155
|
+
- Proposed task list with AC mappings and complexity estimates
|
|
156
|
+
- Risks and dependencies
|
|
157
|
+
- State: "Spec ready for development — see {spec path}. Proposed tasks ready for Jira creation."
|
|
158
|
+
|
|
159
|
+
Do NOT assume a developer will automatically pick up the work.
|
|
160
|
+
|
|
161
|
+
## Hard Rules
|
|
162
|
+
- **NEVER write, edit, or create application code.** You may only write to `docs/specs/` and handoff files. Implementation is the developer's job.
|
|
163
|
+
- **NEVER write to Jira.** Read-only access. Propose tasks; human or PM creates them.
|
|
164
|
+
- **NEVER write to Confluence.** Read-only access for context gathering.
|
|
165
|
+
- Do not add unapproved scope.
|
|
166
|
+
- Do not leave ambiguous criteria or missing AC IDs.
|
|
167
|
+
- Do not allow tasks >= 5 points without documented split review.
|
|
168
|
+
- Do not write specs without developer approval of scope first.
|
|
169
|
+
- NEVER merge PRs. Only human users merge.
|
|
170
|
+
- If blocked >30 minutes, create blocker report.
|
|
171
|
+
|
|
172
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
# Split from lint deliberately: a spec failure and a lint failure are
|
|
10
|
+
# different problems, and each reports its own status check. Specs run
|
|
11
|
+
# across a Ruby matrix; lint does not — rubocop's output does not vary
|
|
12
|
+
# by Ruby, so one run on the floor is enough.
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
# Floor, current, next, and 4.0 — which a consuming application
|
|
20
|
+
# already runs. The floor must stay green for the gemspec's
|
|
21
|
+
# required_ruby_version to mean anything.
|
|
22
|
+
ruby: ['3.1', '3.3', '3.4', '4.0']
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
|
28
|
+
uses: ruby/setup-ruby@v1
|
|
29
|
+
with:
|
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
|
31
|
+
bundler-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Run specs
|
|
34
|
+
run: |
|
|
35
|
+
bundle exec rspec \
|
|
36
|
+
--format progress \
|
|
37
|
+
--format RspecJunitFormatter \
|
|
38
|
+
--out tmp/test-results/rspec.xml
|
|
39
|
+
|
|
40
|
+
# upload-artifact@v4 rejects duplicate names, so each matrix leg
|
|
41
|
+
# needs its own.
|
|
42
|
+
- name: Upload test results
|
|
43
|
+
uses: actions/upload-artifact@v4
|
|
44
|
+
if: ${{ !cancelled() }}
|
|
45
|
+
with:
|
|
46
|
+
name: test-results-ruby-${{ matrix.ruby }}
|
|
47
|
+
path: tmp/test-results/
|
|
48
|
+
|
|
49
|
+
lint:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Set up Ruby
|
|
56
|
+
uses: ruby/setup-ruby@v1
|
|
57
|
+
with:
|
|
58
|
+
ruby-version-file: .ruby-version
|
|
59
|
+
bundler-cache: true
|
|
60
|
+
|
|
61
|
+
- name: Run rubocop
|
|
62
|
+
run: bundle exec rubocop
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
|
2
2
|
|
|
3
|
-
Rails:
|
|
4
|
-
Enabled: false
|
|
5
3
|
AllCops:
|
|
6
|
-
TargetRubyVersion:
|
|
4
|
+
TargetRubyVersion: 3.1
|
|
7
5
|
Exclude:
|
|
8
6
|
- "vendor/**/*"
|
|
9
7
|
- "db/schema.rb"
|
|
10
8
|
- "node_modules/**/*"
|
|
11
9
|
- bin/*
|
|
12
10
|
UseCache: false
|
|
13
|
-
|
|
14
|
-
Description: Checks the position of the dot in multi-line method calls.
|
|
15
|
-
StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
|
|
16
|
-
Enabled: true
|
|
17
|
-
EnforcedStyle: trailing
|
|
18
|
-
SupportedStyles:
|
|
19
|
-
- leading
|
|
20
|
-
- trailing
|
|
11
|
+
SuggestExtensions: false
|
|
21
12
|
Layout/EmptyLineAfterMagicComment:
|
|
22
13
|
Description: 'Add an empty line after magic comments to separate them from code.'
|
|
23
14
|
StyleGuide: '#separate-magic-comments-from-code'
|
|
@@ -66,16 +57,9 @@ Style/PercentLiteralDelimiters:
|
|
|
66
57
|
"%w": "()"
|
|
67
58
|
"%W": "()"
|
|
68
59
|
"%x": "()"
|
|
69
|
-
Naming/
|
|
60
|
+
Naming/PredicatePrefix:
|
|
70
61
|
Description: Check the names of predicate methods.
|
|
71
|
-
StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark
|
|
72
62
|
Enabled: true
|
|
73
|
-
NamePrefix:
|
|
74
|
-
- is_
|
|
75
|
-
- has_
|
|
76
|
-
- have_
|
|
77
|
-
NamePrefixBlacklist:
|
|
78
|
-
- is_
|
|
79
63
|
Exclude:
|
|
80
64
|
- spec/**/*
|
|
81
65
|
Style/RaiseArgs:
|
|
@@ -241,7 +225,7 @@ Style/WhenThen:
|
|
|
241
225
|
Lint/EachWithObjectArgument:
|
|
242
226
|
Description: Check for immutable argument given to each_with_object.
|
|
243
227
|
Enabled: true
|
|
244
|
-
Lint/
|
|
228
|
+
Lint/SuppressedException:
|
|
245
229
|
Description: Don't suppress exception.
|
|
246
230
|
StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions
|
|
247
231
|
Enabled: false
|
|
@@ -252,82 +236,6 @@ Lint/LiteralInInterpolation:
|
|
|
252
236
|
Description: Checks for literals used in interpolation.
|
|
253
237
|
Enabled: false
|
|
254
238
|
|
|
255
|
-
##################### Rails ##################################
|
|
256
|
-
|
|
257
|
-
Rails/ActionFilter:
|
|
258
|
-
Description: 'Enforces consistent use of action filter methods.'
|
|
259
|
-
Enabled: true
|
|
260
|
-
Rails/Date:
|
|
261
|
-
Description: >-
|
|
262
|
-
Checks the correct usage of date aware methods,
|
|
263
|
-
such as Date.today, Date.current etc.
|
|
264
|
-
Enabled: true
|
|
265
|
-
Rails/Delegate:
|
|
266
|
-
Description: 'Prefer delegate method for delegations.'
|
|
267
|
-
Enabled: true
|
|
268
|
-
Rails/Exit:
|
|
269
|
-
Description: >-
|
|
270
|
-
Favor `fail`, `break`, `return`, etc. over `exit` in
|
|
271
|
-
application or library code outside of Rake files to avoid
|
|
272
|
-
exits during unit testing or running in production.
|
|
273
|
-
Enabled: true
|
|
274
|
-
Rails/FindBy:
|
|
275
|
-
Description: 'Prefer find_by over where.first.'
|
|
276
|
-
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#find_by'
|
|
277
|
-
Enabled: true
|
|
278
|
-
Rails/FindEach:
|
|
279
|
-
Description: 'Prefer all.find_each over all.find.'
|
|
280
|
-
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#find-each'
|
|
281
|
-
Enabled: true
|
|
282
|
-
Rails/HasAndBelongsToMany:
|
|
283
|
-
Description: 'Prefer has_many :through to has_and_belongs_to_many.'
|
|
284
|
-
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#has-many-through'
|
|
285
|
-
Enabled: true
|
|
286
|
-
Rails/HttpPositionalArguments:
|
|
287
|
-
Description: 'Rails 5 only, disabled to keep Hound quiet'
|
|
288
|
-
Enabled: false
|
|
289
|
-
Rails/NotNullColumn:
|
|
290
|
-
Description: 'Do not add a NOT NULL column without a default value'
|
|
291
|
-
Enabled: false
|
|
292
|
-
Rails/Output:
|
|
293
|
-
Description: 'Checks for calls to puts, print, etc.'
|
|
294
|
-
Enabled: true
|
|
295
|
-
Rails/OutputSafety:
|
|
296
|
-
Description: 'The use of `html_safe` or `raw` may be a security risk.'
|
|
297
|
-
Enabled: true
|
|
298
|
-
Rails/PluralizationGrammar:
|
|
299
|
-
Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.'
|
|
300
|
-
Enabled: true
|
|
301
|
-
Rails/ReadWriteAttribute:
|
|
302
|
-
Description: >-
|
|
303
|
-
Checks for read_attribute(:attr) and
|
|
304
|
-
write_attribute(:attr, val).
|
|
305
|
-
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#read-attribute'
|
|
306
|
-
Enabled: true
|
|
307
|
-
Rails/RequestReferer:
|
|
308
|
-
Description: 'Use consistent syntax for request.referer.'
|
|
309
|
-
Enabled: true
|
|
310
|
-
Rails/SafeNavigation:
|
|
311
|
-
Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`"
|
|
312
|
-
Enabled: true
|
|
313
|
-
Rails/ScopeArgs:
|
|
314
|
-
Description: 'Checks the arguments of ActiveRecord scopes.'
|
|
315
|
-
Enabled: true
|
|
316
|
-
Rails/TimeZone:
|
|
317
|
-
Description: 'Checks the correct usage of time zone aware methods.'
|
|
318
|
-
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
|
|
319
|
-
Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
|
|
320
|
-
Enabled: true
|
|
321
|
-
Rails/UniqBeforePluck:
|
|
322
|
-
Description: 'Prefer the use of uniq or distinct before pluck.'
|
|
323
|
-
Enabled: true
|
|
324
|
-
Rails/Validation:
|
|
325
|
-
Description: 'Use validates :attribute, hash of validations.'
|
|
326
|
-
Enabled: true
|
|
327
|
-
Security/JSONLoad:
|
|
328
|
-
Description : 'Prefer usage of JSON.parse'
|
|
329
|
-
Enabled: true
|
|
330
|
-
|
|
331
239
|
Layout/DotPosition:
|
|
332
240
|
Description: Checks the position of the dot in multi-line method calls.
|
|
333
241
|
StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
|
|
@@ -354,11 +262,10 @@ Style/ClassAndModuleChildren:
|
|
|
354
262
|
- nested
|
|
355
263
|
Style/MultilineIfModifier:
|
|
356
264
|
Enabled: false
|
|
357
|
-
|
|
358
|
-
Description:
|
|
359
|
-
|
|
265
|
+
Naming/VariableNumber:
|
|
266
|
+
Description: >-
|
|
267
|
+
The codebase consistently uses trailing _1 / _2 to enumerate records,
|
|
268
|
+
mostly in spec `let` names. Match that rather than renaming for the
|
|
269
|
+
sake of the cop's default.
|
|
360
270
|
Enabled: true
|
|
361
|
-
EnforcedStyle:
|
|
362
|
-
SupportedStyles:
|
|
363
|
-
- single_quotes
|
|
364
|
-
- double_quotes
|
|
271
|
+
EnforcedStyle: snake_case
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.1
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Ruby support is expressed through `required_ruby_version` rather than the
|
|
9
|
+
version number — see [docs/RELEASING.md](docs/RELEASING.md). Raising that floor
|
|
10
|
+
is a breaking change and takes a major bump; adding support for a newer Ruby
|
|
11
|
+
takes none.
|
|
12
|
+
|
|
13
|
+
## [1.0.0] - 2026-09-14
|
|
14
|
+
|
|
15
|
+
First release since `v0.19.0`. The `1.0.0` bump declares the public API stable
|
|
16
|
+
and makes semver's major-bump signal available, which `0.x` does not.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `Api::AccountsReceivables::Payments`, adding `#create_payment`,
|
|
21
|
+
`#apply_payment` and `#post_payment` to the client, with the `NewPayment`,
|
|
22
|
+
`NewPaymentRemark` and `PaymentPostResult` XML models. (#44)
|
|
23
|
+
- `Api::AccountsPayables::Disbursements`, adding `#create_disbursement` to the
|
|
24
|
+
client, with the `NewDisbursement` XML model. (#40)
|
|
25
|
+
- `freight_uom` declared on `Api::Grain::NewTicket`. (#43)
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Ruby 3.1 or newer is now required.** The gemspec declares
|
|
30
|
+
`required_ruby_version >= 3.1`; previously it declared no floor at all, so
|
|
31
|
+
the gem could be installed on unsupported Rubies and fail at runtime.
|
|
32
|
+
Applications below 3.1 will resolve to `v0.19.0` and should stay there. (#52)
|
|
33
|
+
- **`*_changed_since` methods now take `detail:` as a keyword argument**
|
|
34
|
+
rather than a positional boolean. Calls such as
|
|
35
|
+
`client.orders_changed_since(time, true)` must become
|
|
36
|
+
`client.orders_changed_since(time, detail: true)`. Affects
|
|
37
|
+
`invoices_changed_since`, `purchase_contracts_changed_since`,
|
|
38
|
+
`sales_contracts_changed_since`, `delivery_tickets_changed_since` and
|
|
39
|
+
`orders_changed_since`. (#57)
|
|
40
|
+
- Versioning moved to a single line. An earlier scheme encoded the supported
|
|
41
|
+
Ruby in the version number (`0.19.x` for Ruby 3.1, `0.2x` for Ruby 3.3);
|
|
42
|
+
it fought semver and RubyGems has no concept of release lines, always serving
|
|
43
|
+
the highest version as `latest`. `0.20.0` and `0.21.0` were never published
|
|
44
|
+
and are superseded by this release. (#52)
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- `Api::PostResult#rejections` no longer raises when a post is processed
|
|
49
|
+
cleanly. A processed post reports an empty `<reject />`, which previously
|
|
50
|
+
produced a `NoMethodError` rather than an empty list. (#44)
|
|
51
|
+
- `bundle exec rake` runs to completion again. RuboCop had been unable to run
|
|
52
|
+
at all since the move to Ruby 3.x — it calls the positional
|
|
53
|
+
`Psych.safe_load` signature removed in Psych 4, and rejected any 3.x value in
|
|
54
|
+
`.ruby-version`. Separately, lint offences had accumulated since February
|
|
55
|
+
2024 because nothing was running the linter. (#52)
|
|
56
|
+
|
|
57
|
+
### Internal
|
|
58
|
+
|
|
59
|
+
- CI migrated from CircleCI to GitHub Actions, running specs and lint as
|
|
60
|
+
separate jobs so each reports its own status. CircleCI ran specs only, which
|
|
61
|
+
is why the RuboCop breakage went unnoticed across three releases' worth of
|
|
62
|
+
commits. (#52)
|
|
63
|
+
- Specs now run against Ruby 3.1, 3.3 and 3.4, so `required_ruby_version` is
|
|
64
|
+
verified rather than asserted. (#53)
|
|
65
|
+
- Release process documented in [docs/RELEASING.md](docs/RELEASING.md). (#54)
|
|
66
|
+
- RuboCop bumped from 0.54.0 to 1.x, retiring four workarounds that existed
|
|
67
|
+
only to keep a 2018 release running on Ruby 3.x — a `psych` pin and three
|
|
68
|
+
disabled or duplicated configuration entries. Ruby 4.0 added to the CI
|
|
69
|
+
matrix, which a consuming application already runs. (#57)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Earlier releases
|
|
74
|
+
|
|
75
|
+
Releases up to and including `v0.19.0` predate this changelog. See the
|
|
76
|
+
[commit history](https://github.com/westernmilling/agris.rb/commits/master)
|
|
77
|
+
and [release tags](https://github.com/westernmilling/agris.rb/tags).
|
|
78
|
+
|
|
79
|
+
[1.0.0]: https://github.com/westernmilling/agris.rb/compare/v0.19.0...v1.0.0
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# CLAUDE.md — Project Configuration
|
|
2
|
+
|
|
3
|
+
## Product Context
|
|
4
|
+
- **Product name:** agris (Ruby gem)
|
|
5
|
+
- **Primary user/persona:** Ruby application developers at Western Milling (and downstream consumers) who integrate with the Agris ERP Web Services SOAP API.
|
|
6
|
+
- **Problem statement:** Agris exposes its ERP data (grain contracts/tickets, inventory orders, delivery tickets, AR invoices, AP vouchers, commodity codes, document tracking) through a single SOAP `ProcessMessage` endpoint. This gem wraps that endpoint with a typed Ruby client so consumers don't construct/parse XML by hand.
|
|
7
|
+
- **Non-goals:**
|
|
8
|
+
- Not a Rails engine — pure library, no UI or persistence.
|
|
9
|
+
- Not a generic SOAP client — only models the Agris message catalog.
|
|
10
|
+
- Does not host or proxy Agris itself; callers supply their own endpoint, dataset, and credentials.
|
|
11
|
+
|
|
12
|
+
## Stack and Dependencies
|
|
13
|
+
- **Languages/frameworks:** Ruby 3.1 (`.ruby-version`, which CI also reads). Rubocop still lints against `TargetRubyVersion: 2.4`.
|
|
14
|
+
- **Key applications/packages:**
|
|
15
|
+
- Runtime: `savon ~> 2.11` (SOAP client).
|
|
16
|
+
- Dev/test: `rspec ~> 3.0`, `rspec_junit_formatter ~> 0.4.1`, `webmock ~> 2.3`, `simplecov 0.17`, `rubocop 0.54.0`, `pry-byebug`, `rake ~> 12.3.3`, `bundler ~> 2.3.7`.
|
|
17
|
+
- **Data layer:** None local. Remote system of record is the Agris ERP database, accessed via its SOAP `ProcessMessage` endpoint (see `lib/agris/savon_request.rb` and `lib/agris/process_message_response.rb`).
|
|
18
|
+
- **Build command:** `bundle install` (or `bin/setup`).
|
|
19
|
+
- **Test command:** `bundle exec rspec` (or `bundle exec rake spec`).
|
|
20
|
+
- **Lint command:** `bundle exec rubocop`.
|
|
21
|
+
- **Dev server command:** N/A (library). For interactive exploration: `bin/console` (boots IRB with the gem loaded).
|
|
22
|
+
|
|
23
|
+
## Verification Commands
|
|
24
|
+
- **Run tests:** `bundle exec rspec`
|
|
25
|
+
- **Run linter:** `bundle exec rubocop`
|
|
26
|
+
- **Type check:** N/A (no Sorbet/RBS in this project)
|
|
27
|
+
- **Run everything (default rake):** `bundle exec rake` — runs `spec` then `rubocop`.
|
|
28
|
+
|
|
29
|
+
## Domain Glossary
|
|
30
|
+
Define key business terms unique to this project so agents use correct terminology.
|
|
31
|
+
|
|
32
|
+
| Term | Definition |
|
|
33
|
+
|------|-----------|
|
|
34
|
+
| Agris | Third-party agribusiness ERP. All API messages target this system. |
|
|
35
|
+
| Dataset | Agris tenancy/company identifier (e.g., `'001'`). Required as a keyword arg on every `Agris::Client.new`; scopes all requests. |
|
|
36
|
+
| Context | `Agris::Context` — endpoint config bundle: base URL, default dataset, dataset path, database name, user, password. |
|
|
37
|
+
| Credentials | `Agris::Credentials::Anonymous` or `Agris::Credentials::BasicAuth` — auth strategy passed to the underlying request. |
|
|
38
|
+
| ProcessMessage | The single SOAP operation Agris exposes; every API call serializes a typed request XML and parses a typed response XML through it. |
|
|
39
|
+
| Request type | Pluggable transport (`Agris::SavonRequest`). Selected via `Agris.request_type`. |
|
|
40
|
+
| XmlModel | `Agris::XmlModel` — base class for declarative request/response XML payloads used across `lib/agris/api/**`. |
|
|
41
|
+
| Grain module | `Agris::Api::Grain::*` — tickets, contracts (purchase/sales), commodity codes, grade factors, rates. |
|
|
42
|
+
| Inventory module | `Agris::Api::Inventory::*` — delivery tickets and orders. |
|
|
43
|
+
| Voucher / Invoice | AP voucher (`Api::AccountsPayables`) and AR invoice (`Api::AccountsReceivables`) postings. |
|
|
44
|
+
| Messages module | `Api::Messages::*` — document-tracking and "changed since" queries (invoices, sales contracts, delivery tickets, orders, commodity-code documents). |
|
|
45
|
+
|
|
46
|
+
## Local Development Setup
|
|
47
|
+
- **Prerequisites:** Ruby 3.1.x and Bundler 2.3+. CI reads `.ruby-version`, so local dev and CI cannot drift; other Ruby versions may work but are unverified.
|
|
48
|
+
- **Install steps:**
|
|
49
|
+
1. `git clone git@github.com:westernmilling/agris.rb.git`
|
|
50
|
+
2. `cd agris.rb`
|
|
51
|
+
3. `bin/setup` (runs `bundle install`)
|
|
52
|
+
- **How to run locally:** This is a library — no server. Use `bin/console` to load the gem in IRB and exercise the client interactively.
|
|
53
|
+
- **How to seed data:** N/A. Tests use fixture XML under `spec/fixtures/agris/**` (stubbed via WebMock); they do not require a live Agris endpoint.
|
|
54
|
+
- **How to run tests:** `bundle exec rspec` (full suite), `bundle exec rspec spec/lib/agris/client/grain/purchase_contracts_spec.rb` (one file), or `bundle exec rake` (specs + rubocop).
|
|
55
|
+
- **Key URLs:**
|
|
56
|
+
- Repo: https://github.com/westernmilling/agris.rb
|
|
57
|
+
- CI: GitHub Actions (workflow at `.github/workflows/ci.yml`).
|
|
58
|
+
- Coverage / quality: Code Climate (badges in `README.md`; config in `.codeclimate.yml`).
|
|
59
|
+
|
|
60
|
+
## Common Gotchas
|
|
61
|
+
- **Ruby 2.4 target.** Rubocop's `TargetRubyVersion` is `2.4`. Avoid syntax/stdlib introduced after 2.4 unless you are intentionally bumping the floor.
|
|
62
|
+
- **Rubocop 0.54.0 is pinned and old.** Many newer cops do not exist; `.rubocop_todo.yml` carries existing exemptions. Don't auto-correct broadly without running the full suite afterward.
|
|
63
|
+
- **`Agris::HTTPartyRequest` is autoloaded but the file is not present** in `lib/agris/`. Only `SavonRequest` is implemented — configure `Agris.request_type = Agris::SavonRequest`. The dangling autoload is a known wart.
|
|
64
|
+
- **`dataset:` is keyword-required on `Client.new`.** `Agris::Client.new(dataset: '001')` — forgetting the keyword raises `ArgumentError`.
|
|
65
|
+
- **All requests funnel through Agris's single `ProcessMessage` SOAP op.** Errors come back in-band as `<MessageStatus>` payloads (see `lib/agris/process_message_response.rb`), not as HTTP errors. Inspect `ApiError` / `MessageError` / `SystemError` / `UnknownError` raised from the client.
|
|
66
|
+
- **Fixtures are XML, sometimes paired with JSON snapshots.** When adding a new endpoint, add both request and response fixtures under `spec/fixtures/agris/<area>/`.
|
|
67
|
+
- **`lib/hash.rb` monkey-patches core `Hash`.** Be careful when refactoring helpers — they may be in use across XML models.
|
|
68
|
+
- **Released to RubyGems as `agris`.** Bumping `lib/agris/version.rb` and running `bundle exec rake release` will tag, push, and publish. Do not run `rake release` casually.
|
|
69
|
+
|
|
70
|
+
## Integrations
|
|
71
|
+
- **Jira project key:** _Not configured for this repo._ <!-- Fill in if/when this gem is tracked in Jira; used for branch naming and issue linking. -->
|
|
72
|
+
- **Staging URL:** N/A (library, no deployed environment).
|
|
73
|
+
- **CI pipeline:** GitHub Actions — `.github/workflows/ci.yml`. Two parallel jobs on pushes to `master` and on every pull request: `test` (`bundle exec rspec`, JUnit output uploaded as an artifact) and `lint` (`bundle exec rubocop`). They report separate status checks so a spec failure and a lint failure are distinguishable at a glance.
|
|
74
|
+
|
|
75
|
+
## Project Overrides
|
|
76
|
+
Rules here override global CLAUDE.md rules for this project.
|
|
77
|
+
|
|
78
|
+
### Architecture Rules
|
|
79
|
+
- **Layering rules:**
|
|
80
|
+
- `lib/agris.rb` — module-level configuration (`Agris.configure`) and autoloads.
|
|
81
|
+
- `lib/agris/client.rb` — single entry point; mixes in API modules from `lib/agris/api/**`.
|
|
82
|
+
- `lib/agris/api/<area>/<resource>.rb` — one Ruby module per Agris message; defines request building and response parsing using `XmlModel`.
|
|
83
|
+
- `lib/agris/{savon_request,process_message_response,context,credentials,...}.rb` — transport, response envelope, config primitives.
|
|
84
|
+
- Add new endpoints by creating a module under the appropriate `api/<area>/` namespace and including it in `Client`.
|
|
85
|
+
- **Dependency boundaries:**
|
|
86
|
+
- API modules must not call `Savon` directly — go through the configured `request_type`.
|
|
87
|
+
- Keep XML construction inside `XmlModel` subclasses, not inline in client methods.
|
|
88
|
+
- No Rails / ActiveSupport assumptions — this is a plain Ruby gem.
|
|
89
|
+
- **Error-handling conventions:**
|
|
90
|
+
- Surface Agris failures as one of the existing `Agris::*Error` classes defined in `lib/agris.rb`. Don't introduce new error hierarchies without discussion.
|
|
91
|
+
- Don't rescue and swallow inside API modules; let the caller decide.
|
|
92
|
+
|
|
93
|
+
### Delivery Rules
|
|
94
|
+
- **Branch strategy:** Trunk-based off `master`. Feature branches per change; PRs target `master`.
|
|
95
|
+
- **PR size policy:** ~200 LOC per PR, one testable behavior. RSpec coverage required for behavior changes; map specs to acceptance criteria when specs exist under `docs/specs/`.
|
|
96
|
+
|
|
97
|
+
### Model Overrides
|
|
98
|
+
_None._
|
data/README.md
CHANGED
|
@@ -52,7 +52,15 @@ Enjoy!
|
|
|
52
52
|
|
|
53
53
|
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.
|
|
54
54
|
|
|
55
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
55
|
+
To install this gem onto your local machine, run `bundle exec rake install`. For releases, see [Releasing](#releasing) below.
|
|
56
|
+
|
|
57
|
+
## Releasing
|
|
58
|
+
|
|
59
|
+
The gem is published to RubyGems as `agris`, on a single version line from
|
|
60
|
+
`master`. Ruby support is expressed through `required_ruby_version` rather than
|
|
61
|
+
the version number, and `rake release` tags, pushes and publishes in one
|
|
62
|
+
irreversible step — see [docs/RELEASING.md](docs/RELEASING.md) before cutting a
|
|
63
|
+
release.
|
|
56
64
|
|
|
57
65
|
## Contributing
|
|
58
66
|
|
data/agris.gemspec
CHANGED
|
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.summary = 'Ruby client library for Agris API'
|
|
13
13
|
spec.homepage = 'https://github.com/westernmilling/agris.rb'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.1'
|
|
15
16
|
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
17
18
|
f.match(%r{^(test|spec|features)/})
|
|
@@ -22,12 +23,12 @@ Gem::Specification.new do |spec|
|
|
|
22
23
|
|
|
23
24
|
spec.add_dependency 'savon', '~> 2.11'
|
|
24
25
|
|
|
25
|
-
spec.add_development_dependency 'bundler', '
|
|
26
|
+
spec.add_development_dependency 'bundler', '>= 2.3'
|
|
26
27
|
spec.add_development_dependency 'pry-byebug'
|
|
27
28
|
spec.add_development_dependency 'rake', '~> 12.3.3'
|
|
28
29
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
29
30
|
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
|
|
30
|
-
spec.add_development_dependency 'rubocop', '
|
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 1.80'
|
|
31
32
|
spec.add_development_dependency 'simplecov', '0.17'
|
|
32
33
|
spec.add_development_dependency 'webmock', '~> 2.3'
|
|
33
34
|
end
|