bunny_farm 0.1.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.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/.github/workflows/docs.yml +38 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +61 -0
- data/COMMITS.md +196 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +330 -0
- data/Rakefile +9 -0
- data/bunny_farm.gemspec +30 -0
- data/config/bunny.yml.erb +29 -0
- data/config/bunny_test.yml.erb +29 -0
- data/config/hipchat.yml.erb +12 -0
- data/docs/api/configuration.md +9 -0
- data/docs/api/consumer.md +8 -0
- data/docs/api/message-class.md +419 -0
- data/docs/api/publisher.md +9 -0
- data/docs/architecture/integration.md +8 -0
- data/docs/architecture/message-flow.md +11 -0
- data/docs/architecture/overview.md +448 -0
- data/docs/architecture/scaling.md +8 -0
- data/docs/assets/actions_dsl_flow.svg +109 -0
- data/docs/assets/architecture_overview.svg +152 -0
- data/docs/assets/best_practices_patterns.svg +203 -0
- data/docs/assets/bunny_farm_logo.png +0 -0
- data/docs/assets/configuration_api_methods.svg +104 -0
- data/docs/assets/configuration_flow.svg +130 -0
- data/docs/assets/configuration_hierarchy.svg +70 -0
- data/docs/assets/data_processing_pipeline.svg +131 -0
- data/docs/assets/debugging_monitoring.svg +165 -0
- data/docs/assets/ecommerce_example_flow.svg +145 -0
- data/docs/assets/email_campaign_example.svg +127 -0
- data/docs/assets/environment_variables_map.svg +78 -0
- data/docs/assets/error_handling_flow.svg +114 -0
- data/docs/assets/favicon.ico +1 -0
- data/docs/assets/fields_dsl_structure.svg +89 -0
- data/docs/assets/instance_methods_lifecycle.svg +137 -0
- data/docs/assets/integration_patterns.svg +207 -0
- data/docs/assets/json_serialization_flow.svg +153 -0
- data/docs/assets/logo.svg +4 -0
- data/docs/assets/message_api_overview.svg +126 -0
- data/docs/assets/message_encapsulation.svg +113 -0
- data/docs/assets/message_lifecycle.svg +110 -0
- data/docs/assets/message_structure.svg +138 -0
- data/docs/assets/publisher_consumer_api.svg +120 -0
- data/docs/assets/scaling_deployment_patterns.svg +195 -0
- data/docs/assets/smart_routing_diagram.svg +131 -0
- data/docs/assets/system_architecture_overview.svg +155 -0
- data/docs/assets/task_scheduling_flow.svg +139 -0
- data/docs/assets/testing_strategies.svg +146 -0
- data/docs/assets/workflow_patterns.svg +183 -0
- data/docs/assets/yaml_config_structure.svg +72 -0
- data/docs/configuration/environment-variables.md +14 -0
- data/docs/configuration/overview.md +373 -0
- data/docs/configuration/programmatic-setup.md +10 -0
- data/docs/configuration/yaml-configuration.md +12 -0
- data/docs/core-features/configuration.md +528 -0
- data/docs/core-features/error-handling.md +82 -0
- data/docs/core-features/json-serialization.md +545 -0
- data/docs/core-features/message-design.md +406 -0
- data/docs/core-features/smart-routing.md +467 -0
- data/docs/core-features/task-scheduling.md +67 -0
- data/docs/core-features/workflow-support.md +112 -0
- data/docs/development/contributing.md +345 -0
- data/docs/development/roadmap.md +9 -0
- data/docs/development/testing.md +14 -0
- data/docs/examples/order-processing.md +10 -0
- data/docs/examples/overview.md +269 -0
- data/docs/examples/real-world.md +8 -0
- data/docs/examples/simple-producer-consumer.md +15 -0
- data/docs/examples/task-scheduler.md +9 -0
- data/docs/getting-started/basic-concepts.md +274 -0
- data/docs/getting-started/installation.md +122 -0
- data/docs/getting-started/quick-start.md +158 -0
- data/docs/index.md +106 -0
- data/docs/message-structure/actions-dsl.md +163 -0
- data/docs/message-structure/fields-dsl.md +146 -0
- data/docs/message-structure/instance-methods.md +115 -0
- data/docs/message-structure/overview.md +211 -0
- data/examples/README.md +212 -0
- data/examples/consumer.rb +41 -0
- data/examples/images/message_flow.svg +87 -0
- data/examples/images/order_workflow.svg +122 -0
- data/examples/images/producer_consumer.svg +96 -0
- data/examples/images/task_scheduler.svg +140 -0
- data/examples/order_processor.rb +238 -0
- data/examples/producer.rb +60 -0
- data/examples/simple_message.rb +43 -0
- data/examples/task_scheduler.rb +263 -0
- data/images/architecture_overview.svg +152 -0
- data/images/bunny_farm_logo.png +0 -0
- data/images/configuration_flow.svg +130 -0
- data/images/message_structure.svg +138 -0
- data/lib/bunny_farm/.irbrc +7 -0
- data/lib/bunny_farm/generic_consumer.rb +12 -0
- data/lib/bunny_farm/hash_ext.rb +37 -0
- data/lib/bunny_farm/init_bunny.rb +137 -0
- data/lib/bunny_farm/init_hipchat.rb +49 -0
- data/lib/bunny_farm/message.rb +218 -0
- data/lib/bunny_farm/message_elements.rb +25 -0
- data/lib/bunny_farm/version.rb +3 -0
- data/lib/bunny_farm.rb +9 -0
- data/mkdocs.yml +148 -0
- metadata +244 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<svg width="1000" height="700" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="1000" height="700" fill="transparent"/>
|
|
3
|
+
|
|
4
|
+
<!-- Title -->
|
|
5
|
+
<text x="500" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#e0e0e0">
|
|
6
|
+
BunnyFarm System Architecture Overview
|
|
7
|
+
</text>
|
|
8
|
+
|
|
9
|
+
<!-- Application Layer -->
|
|
10
|
+
<g>
|
|
11
|
+
<text x="500" y="70" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#68d391">
|
|
12
|
+
Application Layer
|
|
13
|
+
</text>
|
|
14
|
+
|
|
15
|
+
<rect x="50" y="90" width="900" height="120" rx="8" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
16
|
+
|
|
17
|
+
<!-- Web Applications -->
|
|
18
|
+
<rect x="80" y="120" width="150" height="60" rx="6" fill="#1a202c" stroke="#4299e1" stroke-width="1"/>
|
|
19
|
+
<text x="155" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4299e1">Web Applications</text>
|
|
20
|
+
<text x="90" y="165" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Rails, Sinatra, FastAPI</text>
|
|
21
|
+
|
|
22
|
+
<!-- Background Jobs -->
|
|
23
|
+
<rect x="250" y="120" width="150" height="60" rx="6" fill="#1a202c" stroke="#9f7aea" stroke-width="1"/>
|
|
24
|
+
<text x="325" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#9f7aea">Background Jobs</text>
|
|
25
|
+
<text x="260" y="165" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Sidekiq, Resque, Cron</text>
|
|
26
|
+
|
|
27
|
+
<!-- Microservices -->
|
|
28
|
+
<rect x="420" y="120" width="150" height="60" rx="6" fill="#1a202c" stroke="#f6e05e" stroke-width="1"/>
|
|
29
|
+
<text x="495" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">Microservices</text>
|
|
30
|
+
<text x="430" y="165" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Docker, Kubernetes</text>
|
|
31
|
+
|
|
32
|
+
<!-- External APIs -->
|
|
33
|
+
<rect x="590" y="120" width="150" height="60" rx="6" fill="#1a202c" stroke="#e53e3e" stroke-width="1"/>
|
|
34
|
+
<text x="665" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#e53e3e">External APIs</text>
|
|
35
|
+
<text x="600" y="165" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Webhooks, REST APIs</text>
|
|
36
|
+
|
|
37
|
+
<!-- Scheduled Tasks -->
|
|
38
|
+
<rect x="760" y="120" width="150" height="60" rx="6" fill="#1a202c" stroke="#68d391" stroke-width="1"/>
|
|
39
|
+
<text x="835" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">Scheduled Tasks</text>
|
|
40
|
+
<text x="770" y="165" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Reports, Cleanup, ETL</text>
|
|
41
|
+
</g>
|
|
42
|
+
|
|
43
|
+
<!-- BunnyFarm Layer -->
|
|
44
|
+
<g>
|
|
45
|
+
<text x="500" y="260" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#ff9800">
|
|
46
|
+
BunnyFarm Messaging Layer
|
|
47
|
+
</text>
|
|
48
|
+
|
|
49
|
+
<rect x="50" y="280" width="900" height="140" rx="8" fill="#2d3748" stroke="#ff9800" stroke-width="3"/>
|
|
50
|
+
|
|
51
|
+
<!-- Publishers -->
|
|
52
|
+
<rect x="80" y="310" width="180" height="80" rx="6" fill="#1a202c" stroke="#4299e1" stroke-width="2"/>
|
|
53
|
+
<text x="170" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#4299e1">Publishers</text>
|
|
54
|
+
<text x="90" y="355" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Message creation</text>
|
|
55
|
+
<text x="90" y="370" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Routing key generation</text>
|
|
56
|
+
<text x="90" y="385" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Scheduling and delays</text>
|
|
57
|
+
|
|
58
|
+
<!-- Message Definitions -->
|
|
59
|
+
<rect x="280" y="310" width="180" height="80" rx="6" fill="#1a202c" stroke="#68d391" stroke-width="2"/>
|
|
60
|
+
<text x="370" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#68d391">Message Classes</text>
|
|
61
|
+
<text x="290" y="355" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Fields DSL</text>
|
|
62
|
+
<text x="290" y="370" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Actions DSL</text>
|
|
63
|
+
<text x="290" y="385" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Business logic</text>
|
|
64
|
+
|
|
65
|
+
<!-- Configuration -->
|
|
66
|
+
<rect x="480" y="310" width="180" height="80" rx="6" fill="#1a202c" stroke="#9f7aea" stroke-width="2"/>
|
|
67
|
+
<text x="570" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#9f7aea">Configuration</text>
|
|
68
|
+
<text x="490" y="355" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Connection settings</text>
|
|
69
|
+
<text x="490" y="370" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Retry policies</text>
|
|
70
|
+
<text x="490" y="385" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Error handling</text>
|
|
71
|
+
|
|
72
|
+
<!-- Consumers -->
|
|
73
|
+
<rect x="680" y="310" width="180" height="80" rx="6" fill="#1a202c" stroke="#f6e05e" stroke-width="2"/>
|
|
74
|
+
<text x="770" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#f6e05e">Consumers</text>
|
|
75
|
+
<text x="690" y="355" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Message processing</text>
|
|
76
|
+
<text x="690" y="370" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Queue binding</text>
|
|
77
|
+
<text x="690" y="385" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Acknowledgment</text>
|
|
78
|
+
</g>
|
|
79
|
+
|
|
80
|
+
<!-- RabbitMQ Layer -->
|
|
81
|
+
<g>
|
|
82
|
+
<text x="500" y="470" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#2b6cb0">
|
|
83
|
+
RabbitMQ Message Broker
|
|
84
|
+
</text>
|
|
85
|
+
|
|
86
|
+
<rect x="50" y="490" width="900" height="120" rx="8" fill="#1a365d" stroke="#2b6cb0" stroke-width="2"/>
|
|
87
|
+
|
|
88
|
+
<!-- Topic Exchange -->
|
|
89
|
+
<rect x="80" y="520" width="160" height="60" rx="6" fill="#2d3748" stroke="#4299e1" stroke-width="1"/>
|
|
90
|
+
<text x="160" y="545" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4299e1">Topic Exchange</text>
|
|
91
|
+
<text x="90" y="565" font-family="Arial, sans-serif" font-size="10" fill="#bee3f8">Pattern-based routing</text>
|
|
92
|
+
|
|
93
|
+
<!-- Queues -->
|
|
94
|
+
<rect x="260" y="520" width="160" height="60" rx="6" fill="#2d3748" stroke="#68d391" stroke-width="1"/>
|
|
95
|
+
<text x="340" y="545" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">Message Queues</text>
|
|
96
|
+
<text x="270" y="565" font-family="Arial, sans-serif" font-size="10" fill="#bee3f8">Durable, persistent</text>
|
|
97
|
+
|
|
98
|
+
<!-- Dead Letter -->
|
|
99
|
+
<rect x="440" y="520" width="160" height="60" rx="6" fill="#2d3748" stroke="#e53e3e" stroke-width="1"/>
|
|
100
|
+
<text x="520" y="545" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#e53e3e">Dead Letter Queue</text>
|
|
101
|
+
<text x="450" y="565" font-family="Arial, sans-serif" font-size="10" fill="#bee3f8">Failed message handling</text>
|
|
102
|
+
|
|
103
|
+
<!-- Delayed Queue -->
|
|
104
|
+
<rect x="620" y="520" width="160" height="60" rx="6" fill="#2d3748" stroke="#f6e05e" stroke-width="1"/>
|
|
105
|
+
<text x="700" y="545" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">Delayed Queue</text>
|
|
106
|
+
<text x="630" y="565" font-family="Arial, sans-serif" font-size="10" fill="#bee3f8">Scheduled messages</text>
|
|
107
|
+
|
|
108
|
+
<!-- Management UI -->
|
|
109
|
+
<rect x="800" y="520" width="120" height="60" rx="6" fill="#2d3748" stroke="#9f7aea" stroke-width="1"/>
|
|
110
|
+
<text x="860" y="545" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#9f7aea">Management UI</text>
|
|
111
|
+
<text x="810" y="565" font-family="Arial, sans-serif" font-size="10" fill="#bee3f8">Monitoring</text>
|
|
112
|
+
</g>
|
|
113
|
+
|
|
114
|
+
<!-- Infrastructure Layer -->
|
|
115
|
+
<g>
|
|
116
|
+
<text x="500" y="660" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#e0e0e0">
|
|
117
|
+
Infrastructure Layer
|
|
118
|
+
</text>
|
|
119
|
+
|
|
120
|
+
<rect x="150" y="680" width="160" height="25" rx="4" fill="#1a202c" stroke="#68d391" stroke-width="1"/>
|
|
121
|
+
<text x="230" y="697" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#68d391">Docker Containers</text>
|
|
122
|
+
|
|
123
|
+
<rect x="330" y="680" width="160" height="25" rx="4" fill="#1a202c" stroke="#4299e1" stroke-width="1"/>
|
|
124
|
+
<text x="410" y="697" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#4299e1">Kubernetes Cluster</text>
|
|
125
|
+
|
|
126
|
+
<rect x="510" y="680" width="160" height="25" rx="4" fill="#1a202c" stroke="#f6e05e" stroke-width="1"/>
|
|
127
|
+
<text x="590" y="697" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#f6e05e">Load Balancer</text>
|
|
128
|
+
|
|
129
|
+
<rect x="690" y="680" width="160" height="25" rx="4" fill="#1a202c" stroke="#9f7aea" stroke-width="1"/>
|
|
130
|
+
<text x="770" y="697" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#9f7aea">Monitoring Stack</text>
|
|
131
|
+
</g>
|
|
132
|
+
|
|
133
|
+
<!-- Data Flow Arrows -->
|
|
134
|
+
<defs>
|
|
135
|
+
<marker id="arrow-arch" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
136
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="#ff9800"/>
|
|
137
|
+
</marker>
|
|
138
|
+
</defs>
|
|
139
|
+
|
|
140
|
+
<!-- App to BunnyFarm -->
|
|
141
|
+
<line x1="155" y1="210" x2="155" y2="280" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
142
|
+
<line x1="325" y1="210" x2="325" y2="280" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
143
|
+
<line x1="495" y1="210" x2="495" y2="280" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
144
|
+
<line x1="665" y1="210" x2="665" y2="280" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
145
|
+
<line x1="835" y1="210" x2="835" y2="280" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
146
|
+
|
|
147
|
+
<!-- BunnyFarm to RabbitMQ -->
|
|
148
|
+
<line x1="170" y1="420" x2="170" y2="490" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
149
|
+
<line x1="370" y1="420" x2="370" y2="490" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
150
|
+
<line x1="570" y1="420" x2="570" y2="490" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
151
|
+
<line x1="770" y1="420" x2="770" y2="490" stroke="#ff9800" stroke-width="3" marker-end="url(#arrow-arch)"/>
|
|
152
|
+
|
|
153
|
+
<!-- Bidirectional arrows for consumers -->
|
|
154
|
+
<line x1="770" y1="280" x2="770" y2="210" stroke="#68d391" stroke-width="2" marker-end="url(#arrow-arch)"/>
|
|
155
|
+
</svg>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="900" height="500" fill="transparent"/>
|
|
3
|
+
|
|
4
|
+
<!-- Title -->
|
|
5
|
+
<text x="450" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#e0e0e0">
|
|
6
|
+
Task Scheduling and Delayed Execution
|
|
7
|
+
</text>
|
|
8
|
+
|
|
9
|
+
<!-- Timeline -->
|
|
10
|
+
<line x1="100" y1="80" x2="800" y2="80" stroke="#666" stroke-width="2"/>
|
|
11
|
+
<text x="450" y="70" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#9ca3af">Timeline</text>
|
|
12
|
+
|
|
13
|
+
<!-- Time markers -->
|
|
14
|
+
<line x1="150" y1="75" x2="150" y2="85" stroke="#666" stroke-width="1"/>
|
|
15
|
+
<text x="150" y="100" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">Now</text>
|
|
16
|
+
|
|
17
|
+
<line x1="350" y1="75" x2="350" y2="85" stroke="#666" stroke-width="1"/>
|
|
18
|
+
<text x="350" y="100" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">+5 min</text>
|
|
19
|
+
|
|
20
|
+
<line x1="550" y1="75" x2="550" y2="85" stroke="#666" stroke-width="1"/>
|
|
21
|
+
<text x="550" y="100" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">+1 hour</text>
|
|
22
|
+
|
|
23
|
+
<line x1="750" y1="75" x2="750" y2="85" stroke="#666" stroke-width="1"/>
|
|
24
|
+
<text x="750" y="100" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">+1 day</text>
|
|
25
|
+
|
|
26
|
+
<!-- Immediate Processing -->
|
|
27
|
+
<g>
|
|
28
|
+
<rect x="80" y="130" width="140" height="60" rx="8" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
29
|
+
<text x="150" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">Immediate</text>
|
|
30
|
+
<text x="90" y="170" font-family="monospace" font-size="10" fill="#e0e0e0">message.publish('send')</text>
|
|
31
|
+
<text x="90" y="185" font-family="Arial, sans-serif" font-size="9" fill="#9ca3af">Processes right away</text>
|
|
32
|
+
</g>
|
|
33
|
+
|
|
34
|
+
<!-- Delayed Processing -->
|
|
35
|
+
<g>
|
|
36
|
+
<rect x="280" y="130" width="140" height="60" rx="8" fill="#2d3748" stroke="#4299e1" stroke-width="2"/>
|
|
37
|
+
<text x="350" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4299e1">Short Delay</text>
|
|
38
|
+
<text x="290" y="170" font-family="monospace" font-size="10" fill="#e0e0e0">message.publish_at(</text>
|
|
39
|
+
<text x="300" y="185" font-family="monospace" font-size="10" fill="#e0e0e0">5.minutes.from_now)</text>
|
|
40
|
+
</g>
|
|
41
|
+
|
|
42
|
+
<!-- Scheduled Processing -->
|
|
43
|
+
<g>
|
|
44
|
+
<rect x="480" y="130" width="140" height="60" rx="8" fill="#2d3748" stroke="#f6e05e" stroke-width="2"/>
|
|
45
|
+
<text x="550" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">Scheduled</text>
|
|
46
|
+
<text x="490" y="170" font-family="monospace" font-size="10" fill="#e0e0e0">message.schedule(</text>
|
|
47
|
+
<text x="500" y="185" font-family="monospace" font-size="10" fill="#e0e0e0">'0 9 * * 1')</text>
|
|
48
|
+
</g>
|
|
49
|
+
|
|
50
|
+
<!-- Long Delay -->
|
|
51
|
+
<g>
|
|
52
|
+
<rect x="680" y="130" width="140" height="60" rx="8" fill="#2d3748" stroke="#9f7aea" stroke-width="2"/>
|
|
53
|
+
<text x="750" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#9f7aea">Long Delay</text>
|
|
54
|
+
<text x="690" y="170" font-family="monospace" font-size="10" fill="#e0e0e0">message.publish_in(</text>
|
|
55
|
+
<text x="700" y="185" font-family="monospace" font-size="10" fill="#e0e0e0">1.day)</text>
|
|
56
|
+
</g>
|
|
57
|
+
|
|
58
|
+
<!-- Retry Pattern -->
|
|
59
|
+
<g>
|
|
60
|
+
<text x="450" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#e0e0e0">
|
|
61
|
+
Retry Pattern with Exponential Backoff
|
|
62
|
+
</text>
|
|
63
|
+
|
|
64
|
+
<!-- Initial attempt -->
|
|
65
|
+
<rect x="50" y="280" width="100" height="50" rx="6" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
66
|
+
<text x="100" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#68d391">Initial</text>
|
|
67
|
+
<text x="100" y="315" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Attempt</text>
|
|
68
|
+
|
|
69
|
+
<!-- Arrow -->
|
|
70
|
+
<defs>
|
|
71
|
+
<marker id="arrow-retry" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
72
|
+
<polygon points="0 0, 8 3, 0 6" fill="#e53e3e"/>
|
|
73
|
+
</marker>
|
|
74
|
+
</defs>
|
|
75
|
+
|
|
76
|
+
<line x1="160" y1="305" x2="200" y2="305" stroke="#e53e3e" stroke-width="2" marker-end="url(#arrow-retry)"/>
|
|
77
|
+
<text x="180" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e53e3e">FAIL</text>
|
|
78
|
+
|
|
79
|
+
<!-- Retry 1 -->
|
|
80
|
+
<rect x="210" y="280" width="100" height="50" rx="6" fill="#2d3748" stroke="#f6e05e" stroke-width="2"/>
|
|
81
|
+
<text x="260" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#f6e05e">Retry 1</text>
|
|
82
|
+
<text x="260" y="315" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">+2 seconds</text>
|
|
83
|
+
|
|
84
|
+
<line x1="320" y1="305" x2="360" y2="305" stroke="#e53e3e" stroke-width="2" marker-end="url(#arrow-retry)"/>
|
|
85
|
+
<text x="340" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e53e3e">FAIL</text>
|
|
86
|
+
|
|
87
|
+
<!-- Retry 2 -->
|
|
88
|
+
<rect x="370" y="280" width="100" height="50" rx="6" fill="#2d3748" stroke="#4299e1" stroke-width="2"/>
|
|
89
|
+
<text x="420" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4299e1">Retry 2</text>
|
|
90
|
+
<text x="420" y="315" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">+4 seconds</text>
|
|
91
|
+
|
|
92
|
+
<line x1="480" y1="305" x2="520" y2="305" stroke="#e53e3e" stroke-width="2" marker-end="url(#arrow-retry)"/>
|
|
93
|
+
<text x="500" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e53e3e">FAIL</text>
|
|
94
|
+
|
|
95
|
+
<!-- Retry 3 -->
|
|
96
|
+
<rect x="530" y="280" width="100" height="50" rx="6" fill="#2d3748" stroke="#9f7aea" stroke-width="2"/>
|
|
97
|
+
<text x="580" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#9f7aea">Retry 3</text>
|
|
98
|
+
<text x="580" y="315" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">+8 seconds</text>
|
|
99
|
+
|
|
100
|
+
<line x1="640" y1="305" x2="680" y2="305" stroke="#68d391" stroke-width="2" marker-end="url(#arrow-retry)"/>
|
|
101
|
+
<text x="660" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#68d391">SUCCESS</text>
|
|
102
|
+
|
|
103
|
+
<!-- Final success -->
|
|
104
|
+
<rect x="690" y="280" width="100" height="50" rx="6" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
105
|
+
<text x="740" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#68d391">Complete</text>
|
|
106
|
+
<text x="740" y="315" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">✓ Success</text>
|
|
107
|
+
</g>
|
|
108
|
+
|
|
109
|
+
<!-- Use Cases -->
|
|
110
|
+
<g>
|
|
111
|
+
<text x="450" y="380" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#e0e0e0">
|
|
112
|
+
Common Use Cases
|
|
113
|
+
</text>
|
|
114
|
+
|
|
115
|
+
<!-- Email campaigns -->
|
|
116
|
+
<rect x="50" y="400" width="150" height="60" rx="6" fill="#1a202c" stroke="#e53e3e" stroke-width="1"/>
|
|
117
|
+
<text x="125" y="420" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#e53e3e">Email Campaigns</text>
|
|
118
|
+
<text x="60" y="435" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Send welcome series</text>
|
|
119
|
+
<text x="60" y="450" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Drip campaigns</text>
|
|
120
|
+
|
|
121
|
+
<!-- Reports -->
|
|
122
|
+
<rect x="220" y="400" width="150" height="60" rx="6" fill="#1a202c" stroke="#f6e05e" stroke-width="1"/>
|
|
123
|
+
<text x="295" y="420" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#f6e05e">Reports</text>
|
|
124
|
+
<text x="230" y="435" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Daily summaries</text>
|
|
125
|
+
<text x="230" y="450" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Monthly analytics</text>
|
|
126
|
+
|
|
127
|
+
<!-- Cleanup -->
|
|
128
|
+
<rect x="390" y="400" width="150" height="60" rx="6" fill="#1a202c" stroke="#4299e1" stroke-width="1"/>
|
|
129
|
+
<text x="465" y="420" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4299e1">Cleanup Tasks</text>
|
|
130
|
+
<text x="400" y="435" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Remove old files</text>
|
|
131
|
+
<text x="400" y="450" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Archive data</text>
|
|
132
|
+
|
|
133
|
+
<!-- Reminders -->
|
|
134
|
+
<rect x="560" y="400" width="150" height="60" rx="6" fill="#1a202c" stroke="#9f7aea" stroke-width="1"/>
|
|
135
|
+
<text x="635" y="420" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#9f7aea">Reminders</text>
|
|
136
|
+
<text x="570" y="435" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Payment due</text>
|
|
137
|
+
<text x="570" y="450" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Subscription renewal</text>
|
|
138
|
+
</g>
|
|
139
|
+
</svg>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<svg width="900" height="700" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="900" height="700" fill="transparent"/>
|
|
3
|
+
|
|
4
|
+
<!-- Title -->
|
|
5
|
+
<text x="450" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#e0e0e0">
|
|
6
|
+
Testing Strategies for BunnyFarm Applications
|
|
7
|
+
</text>
|
|
8
|
+
|
|
9
|
+
<!-- Testing Pyramid -->
|
|
10
|
+
<g>
|
|
11
|
+
<text x="450" y="70" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#4299e1">
|
|
12
|
+
Testing Pyramid
|
|
13
|
+
</text>
|
|
14
|
+
|
|
15
|
+
<!-- Unit Tests (Base) -->
|
|
16
|
+
<rect x="200" y="200" width="400" height="60" rx="6" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
17
|
+
<text x="400" y="225" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#68d391">Unit Tests</text>
|
|
18
|
+
<text x="400" y="245" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#e0e0e0">Message Classes, Business Logic, Validation</text>
|
|
19
|
+
<text x="400" y="260" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#9ca3af">Fast • Isolated • High Coverage</text>
|
|
20
|
+
|
|
21
|
+
<!-- Integration Tests (Middle) -->
|
|
22
|
+
<rect x="250" y="130" width="300" height="60" rx="6" fill="#2d3748" stroke="#4299e1" stroke-width="2"/>
|
|
23
|
+
<text x="400" y="155" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#4299e1">Integration Tests</text>
|
|
24
|
+
<text x="400" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#e0e0e0">Message Publishing, Queue Processing</text>
|
|
25
|
+
<text x="400" y="190" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#9ca3af">Medium Speed • Component Level</text>
|
|
26
|
+
|
|
27
|
+
<!-- E2E Tests (Top) -->
|
|
28
|
+
<rect x="300" y="90" width="200" height="40" rx="6" fill="#2d3748" stroke="#9f7aea" stroke-width="2"/>
|
|
29
|
+
<text x="400" y="115" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#9f7aea">End-to-End Tests</text>
|
|
30
|
+
<text x="400" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#9ca3af">Full Workflow • Slow • Critical Paths</text>
|
|
31
|
+
</g>
|
|
32
|
+
|
|
33
|
+
<!-- Unit Testing -->
|
|
34
|
+
<g>
|
|
35
|
+
<text x="200" y="320" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#68d391">
|
|
36
|
+
Unit Testing
|
|
37
|
+
</text>
|
|
38
|
+
|
|
39
|
+
<rect x="50" y="340" width="300" height="140" rx="6" fill="#2d3748" stroke="#68d391" stroke-width="1"/>
|
|
40
|
+
<text x="200" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#68d391">Message Class Testing</text>
|
|
41
|
+
|
|
42
|
+
<text x="70" y="385" font-family="monospace" font-size="9" fill="#9ca3af"># RSpec example</text>
|
|
43
|
+
<text x="70" y="400" font-family="monospace" font-size="9" fill="#e0e0e0">describe OrderMessage do</text>
|
|
44
|
+
<text x="80" y="415" font-family="monospace" font-size="9" fill="#e0e0e0">let(:message) do</text>
|
|
45
|
+
<text x="90" y="430" font-family="monospace" font-size="9" fill="#e0e0e0">OrderMessage.new(</text>
|
|
46
|
+
<text x="100" y="445" font-family="monospace" font-size="9" fill="#e0e0e0">order_id: 123,</text>
|
|
47
|
+
<text x="100" y="460" font-family="monospace" font-size="9" fill="#e0e0e0">total: 99.99)</text>
|
|
48
|
+
<text x="80" y="475" font-family="monospace" font-size="9" fill="#e0e0e0">end</text>
|
|
49
|
+
|
|
50
|
+
<text x="70" y="500" font-family="monospace" font-size="9" fill="#e0e0e0">it 'validates required fields'</text>
|
|
51
|
+
<text x="70" y="515" font-family="monospace" font-size="9" fill="#e0e0e0">it 'calculates totals correctly'</text>
|
|
52
|
+
</g>
|
|
53
|
+
|
|
54
|
+
<!-- Integration Testing -->
|
|
55
|
+
<g>
|
|
56
|
+
<text x="450" y="320" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#4299e1">
|
|
57
|
+
Integration Testing
|
|
58
|
+
</text>
|
|
59
|
+
|
|
60
|
+
<rect x="380" y="340" width="300" height="140" rx="6" fill="#2d3748" stroke="#4299e1" stroke-width="1"/>
|
|
61
|
+
<text x="530" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#4299e1">Publisher/Consumer Testing</text>
|
|
62
|
+
|
|
63
|
+
<text x="400" y="385" font-family="monospace" font-size="9" fill="#9ca3af"># Integration test</text>
|
|
64
|
+
<text x="400" y="400" font-family="monospace" font-size="9" fill="#e0e0e0">it 'publishes and processes' do</text>
|
|
65
|
+
<text x="410" y="415" font-family="monospace" font-size="9" fill="#e0e0e0">message = OrderMessage.new</text>
|
|
66
|
+
<text x="410" y="430" font-family="monospace" font-size="9" fill="#e0e0e0">message.publish('process')</text>
|
|
67
|
+
<text x="410" y="445" font-family="monospace" font-size="9" fill="#e0e0e0">expect(queue).to have</text>
|
|
68
|
+
<text x="420" y="460" font-family="monospace" font-size="9" fill="#e0e0e0">_received_message</text>
|
|
69
|
+
<text x="400" y="475" font-family="monospace" font-size="9" fill="#e0e0e0">end</text>
|
|
70
|
+
|
|
71
|
+
<text x="400" y="500" font-family="monospace" font-size="9" fill="#e0e0e0">• Test with test queues</text>
|
|
72
|
+
<text x="400" y="515" font-family="monospace" font-size="9" fill="#e0e0e0">• Mock external services</text>
|
|
73
|
+
</g>
|
|
74
|
+
|
|
75
|
+
<!-- Test Doubles -->
|
|
76
|
+
<g>
|
|
77
|
+
<text x="750" y="320" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#9f7aea">
|
|
78
|
+
Test Doubles
|
|
79
|
+
</text>
|
|
80
|
+
|
|
81
|
+
<rect x="620" y="340" width="260" height="140" rx="6" fill="#2d3748" stroke="#9f7aea" stroke-width="1"/>
|
|
82
|
+
<text x="750" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#9f7aea">Mocking Strategies</text>
|
|
83
|
+
|
|
84
|
+
<!-- Mock Types -->
|
|
85
|
+
<text x="640" y="385" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#f6e05e">Message Mocks:</text>
|
|
86
|
+
<text x="650" y="400" font-family="monospace" font-size="9" fill="#e0e0e0">allow(message)</text>
|
|
87
|
+
<text x="660" y="415" font-family="monospace" font-size="9" fill="#e0e0e0">.to receive(:publish)</text>
|
|
88
|
+
|
|
89
|
+
<text x="640" y="440" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#f6e05e">Queue Stubs:</text>
|
|
90
|
+
<text x="650" y="455" font-family="monospace" font-size="9" fill="#e0e0e0">stub_queue_consumer</text>
|
|
91
|
+
<text x="660" y="470" font-family="monospace" font-size="9" fill="#e0e0e0">expect_message_count(5)</text>
|
|
92
|
+
|
|
93
|
+
<text x="640" y="495" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#f6e05e">Test Adapters:</text>
|
|
94
|
+
<text x="650" y="510" font-family="monospace" font-size="9" fill="#e0e0e0">BunnyFarm.test_mode!</text>
|
|
95
|
+
</g>
|
|
96
|
+
|
|
97
|
+
<!-- Testing Tools -->
|
|
98
|
+
<g>
|
|
99
|
+
<text x="450" y="540" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#e0e0e0">
|
|
100
|
+
Testing Tools and Helpers
|
|
101
|
+
</text>
|
|
102
|
+
|
|
103
|
+
<!-- RSpec -->
|
|
104
|
+
<rect x="50" y="560" width="180" height="100" rx="6" fill="#1a202c" stroke="#68d391" stroke-width="1"/>
|
|
105
|
+
<text x="140" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">RSpec Integration</text>
|
|
106
|
+
<text x="60" y="600" font-family="monospace" font-size="9" fill="#e0e0e0">require 'bunny_farm/rspec'</text>
|
|
107
|
+
<text x="60" y="620" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Message matchers</text>
|
|
108
|
+
<text x="60" y="635" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Queue helpers</text>
|
|
109
|
+
<text x="60" y="650" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Test configuration</text>
|
|
110
|
+
|
|
111
|
+
<!-- Test Environment -->
|
|
112
|
+
<rect x="250" y="560" width="180" height="100" rx="6" fill="#1a202c" stroke="#4299e1" stroke-width="1"/>
|
|
113
|
+
<text x="340" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4299e1">Test Environment</text>
|
|
114
|
+
<text x="260" y="600" font-family="monospace" font-size="9" fill="#e0e0e0">BUNNY_FARM_ENV=test</text>
|
|
115
|
+
<text x="260" y="620" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Isolated queues</text>
|
|
116
|
+
<text x="260" y="635" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• In-memory broker</text>
|
|
117
|
+
<text x="260" y="650" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Fast execution</text>
|
|
118
|
+
|
|
119
|
+
<!-- Coverage -->
|
|
120
|
+
<rect x="450" y="560" width="180" height="100" rx="6" fill="#1a202c" stroke="#9f7aea" stroke-width="1"/>
|
|
121
|
+
<text x="540" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#9f7aea">Coverage Tools</text>
|
|
122
|
+
<text x="460" y="600" font-family="monospace" font-size="9" fill="#e0e0e0">gem 'simplecov'</text>
|
|
123
|
+
<text x="460" y="620" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Line coverage</text>
|
|
124
|
+
<text x="460" y="635" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Branch coverage</text>
|
|
125
|
+
<text x="460" y="650" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Message path coverage</text>
|
|
126
|
+
|
|
127
|
+
<!-- Performance -->
|
|
128
|
+
<rect x="650" y="560" width="180" height="100" rx="6" fill="#1a202c" stroke="#f6e05e" stroke-width="1"/>
|
|
129
|
+
<text x="740" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">Performance Testing</text>
|
|
130
|
+
<text x="660" y="600" font-family="monospace" font-size="9" fill="#e0e0e0">gem 'benchmark-ips'</text>
|
|
131
|
+
<text x="660" y="620" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Message throughput</text>
|
|
132
|
+
<text x="660" y="635" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Queue performance</text>
|
|
133
|
+
<text x="660" y="650" font-family="Arial, sans-serif" font-size="9" fill="#e0e0e0">• Memory usage</text>
|
|
134
|
+
</g>
|
|
135
|
+
|
|
136
|
+
<!-- Connecting lines -->
|
|
137
|
+
<defs>
|
|
138
|
+
<marker id="arrow-test" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
139
|
+
<polygon points="0 0, 8 3, 0 6" fill="#ff9800"/>
|
|
140
|
+
</marker>
|
|
141
|
+
</defs>
|
|
142
|
+
|
|
143
|
+
<!-- Pyramid connections -->
|
|
144
|
+
<line x1="400" y1="190" x2="400" y2="130" stroke="#ff9800" stroke-width="1" marker-end="url(#arrow-test)"/>
|
|
145
|
+
<line x1="400" y1="260" x2="400" y2="200" stroke="#ff9800" stroke-width="1" marker-end="url(#arrow-test)"/>
|
|
146
|
+
</svg>
|