trailblazer-loader 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc05571c5342cd3e10a403596b59b87610bf1a84
4
- data.tar.gz: c4277f78ae6dacb7dbe9253e89da03cbbbdf3547
3
+ metadata.gz: 8f993eca956a001ec251af5f8635dc5dd74ea5e0
4
+ data.tar.gz: 26a5ae8a81b39c05ac8de3bb81629d11ec1a456e
5
5
  SHA512:
6
- metadata.gz: 47ba85836e3f64b7be756f212f2d37aeeae77d16d7ca8b264a8a1337f6e8216d4e8d88eccaed4a22b4ad1fede3c68c2002f4926678778f4bc22cb7ab0f69e639
7
- data.tar.gz: d225d6e85f51363e24c582e63056c6aca2e018cc119206be4dd50aaf4bd629a2ced9cf9cb04d34ca3f890931f149438a63260339741b003f61844a6b2ae51a5e
6
+ metadata.gz: 40b4ecbc9a013b6ba73094130b85d31221fdfd8aff7f657248aa69a40707c68626dac426da94c2ce531ff346592b1020864fc7e6204fbf550e993853dfde4338
7
+ data.tar.gz: 7b53184a5e2558b3d116cb53fb70c2f5a9259e5e3cc42dcec6f898886fd17291c50f5d0d8178a309093f2c8a0127dcd16b87cd047eae55da6845fcad65e7be0f
data/CHANGES.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.0.4
2
+
3
+ * Fix loading for explicit layouts.
4
+
1
5
  # 0.0.3
2
6
 
3
7
  * Bump to Representable 2.4.0 as we use `Pipeline::Collect`. This doesn't mean this dependency will last forever. We might switch to `call_sheet` for pipelines.
data/README.md CHANGED
@@ -1,31 +1,176 @@
1
1
  # Trailblazer::Loader
2
2
 
3
- ## Experimental!
3
+ _Generic loader for Trailblazer projects._
4
4
 
5
- Note that this is not the finalized version of `trailblazer-loader`. We still need more input from you.
5
+ Reportedly works with Rails, Grape, Lotus, and Roda, and many more, for sure.
6
6
 
7
- Expect many minor version bumps.
7
+ **Rails users**: This gem is bundled with [trailblazer-rails](https://github.com/trailblazer/trailblazer-rails).
8
8
 
9
- ## Loading order
9
+ ## Overview
10
+
11
+ While Trailblazer enforces a new file structure where you organize by concept, and not by technology, the naming and the structuring within each concept allows different styles.
12
+
13
+ Trailblazer-loader supports the following directory layouts concurrently.
14
+
15
+ ## Compound-Singular
16
+
17
+ Per concept, you have one file per abstraction layer (called a _compound_ file). All is singular and reflects the namespace (except for operations which sit in the concept's namespace).
18
+
19
+ ```
20
+ app
21
+ ├── concepts
22
+ │   ├── comment
23
+ │   │   ├── callback.rb
24
+ │   │   ├── cell.rb
25
+ │   │   ├── contract.rb
26
+ │   │   ├── operation.rb
27
+ │   │   ├── policy.rb
28
+ │   │   └── views
29
+ │   │   ├── grid.haml
30
+ │   │   └── show.haml
31
+ ```
32
+
33
+ You may nest concepts in concepts.
34
+
35
+ ```
36
+ app
37
+ ├── concepts
38
+ │   ├── comment
39
+ │   │   ├── contract.rb
40
+ │   │   ├── operation.rb
41
+ │   │   ├── admin
42
+ │   │   ├── contract.rb
43
+ │   │   └── operation.rb
44
+ ```
45
+
46
+ Note: This is the structuring used in the [Trailblazer book](http://trailblazer.to/books/trailblazer.html).
47
+
48
+
49
+ ## Explicit-Singular
10
50
 
11
- Per concept. Lexically sorted. Then, sorted by depths, as follows.
51
+ Per concept, you have one directory per abstraction layer and one file per class. All is singular and reflects the namespace (except for operations which sit in the concept's namespace).
12
52
 
13
53
  ```
14
- app/concepts/blog/operation.rb
15
- app/concepts/comment/operation.rb
16
- app/concepts/post/operation.rb
17
- app/concepts/api/v1/comment/operation.rb
18
- app/concepts/api/v1/post/operation.rb
54
+ app
55
+ ├── concepts
56
+ │   ├── comment
57
+ │   │   ├── contract
58
+ │   │   │ ├── create.rb
59
+ │   │   │ └── update.rb
60
+ │   │   ├── cell
61
+ │   │   │ └── form.rb
62
+ │   │   ├── operation
63
+ │   │   │ ├── create.rb
64
+ │   │   │ └── update.rb
65
+ │   │   └── views
66
+ │   │   ├── grid.haml
67
+ │   │   └── show.haml
19
68
  ```
20
69
 
21
- 1. Model. The model does not have dependencies to other layers. If it does, you're doing it wrong.
22
- 2. Policy
23
- 3. Representer
24
- 4. Form
25
- 5. Callbacks, etc.
26
- 6. Operation. As an orchestrating object, this needs to be loaded last. It is very common for operations to reference form classes, etc. on the class level.
27
- first create.rb
28
- then operations.rb {configurable, could also be crud.rb}
70
+ You may nest concepts in concepts.
71
+
72
+ ```
73
+ app
74
+ ├── concepts
75
+ │   ├── comment
76
+ │   │   ├── contract
77
+ │   │   │ ├── create.rb
78
+ │   │   │ └── update.rb
79
+ │   │   ├── operation
80
+ │   │   │ ├── create.rb
81
+ │   │   │ └── update.rb
82
+ │   │   ├── admin
83
+ │   │   │   └── contract
84
+ │   │   │   ├── create.rb
85
+ │   │   │   └── update.rb
86
+ ```
87
+
88
+ ## Explicit-Plural
89
+
90
+ Per concept, you have one pluralized directory per abstraction layer and one file per class.
91
+
92
+ ```
93
+ app
94
+ ├── concepts
95
+ │   ├── comment
96
+ │   │   ├── contracts
97
+ │   │   │ ├── create.rb
98
+ │   │   │ └── update.rb
99
+ │   │   ├── cells
100
+ │   │   │ └── form.rb
101
+ │   │   ├── operations
102
+ │   │   │ ├── create.rb
103
+ │   │   │ └── update.rb
104
+ │   │   └── views
105
+ │   │   ├── grid.haml
106
+ │   │   └── show.haml
107
+ ```
108
+
109
+ And, yes, you may nest concepts in concepts.
110
+
111
+ ```
112
+ app
113
+ ├── concepts
114
+ │   ├── comment
115
+ │   │   ├── contracts
116
+ │   │   │ ├── create.rb
117
+ │   │   │ └── update.rb
118
+ │   │   ├── operation
119
+ │   │   │ ├── create.rb
120
+ │   │   │ └── update.rb
121
+ │   │   ├── admin
122
+ │   │   │   └── contracts
123
+ │   │   │   ├── create.rb
124
+ │   │   │   └── update.rb
125
+ ```
126
+
127
+
128
+ ## Loading order
129
+
130
+ The loading order is identical for all styles.
131
+
132
+ 1. The loader finds all concept directories.
133
+ 2. Concept directories are sorted by nesting level, deeper nestings are loaded later as they might reference concepts they're nested in. For example, `concepts/comment/admin` might reuse existing code from `concepts/comment`.
134
+ 3. Per concept, files are lexically sorted, e.g. `create.rb` will be loaded *before* `update.rb` as we mostly do `Update < Create`.
135
+ 4. Per concept, operation files will be loaded after all other layer files have been required. This is because abstraction files like representers or contracts should not reference their operation. The operation, howver, as an orchestrating asset needs to refer to various abstraction objects.
136
+
137
+ Here's a sample of a explicit-singular session.
138
+
139
+ ```ruby
140
+ [
141
+ "app/concepts/navigation/cell.rb",
142
+ "app/concepts/session/impersonate.rb",
143
+ "app/concepts/session/operation.rb",
144
+ "app/concepts/user/operation.rb",
145
+ "app/concepts/comment/cell/cell.rb",
146
+ "app/concepts/comment/cell/grid.rb",
147
+ "app/concepts/comment/operation/create.rb",
148
+ "app/concepts/api/v1.rb",
149
+ "app/concepts/thing/callback/default.rb",
150
+ "app/concepts/thing/callback/upload.rb",
151
+ "app/concepts/thing/cell.rb",
152
+ "app/concepts/thing/cell/decorator.rb",
153
+ "app/concepts/thing/cell/form.rb",
154
+ "app/concepts/thing/cell/grid.rb",
155
+ "app/concepts/thing/contract/create.rb",
156
+ "app/concepts/thing/contract/update.rb",
157
+ "app/concepts/thing/policy.rb",
158
+ "app/concepts/thing/signed_in.rb",
159
+ "app/concepts/thing/operation/create.rb",
160
+ "app/concepts/thing/operation/delete.rb",
161
+ "app/concepts/thing/operation/show.rb",
162
+ "app/concepts/thing/operation/update.rb",
163
+ "app/concepts/api/v1/comment/representer/show.rb",
164
+ "app/concepts/api/v1/comment/operation/create.rb",
165
+ "app/concepts/api/v1/comment/operation/show.rb",
166
+ "app/concepts/api/v1/thing/representer/create.rb",
167
+ "app/concepts/api/v1/thing/representer/index.rb",
168
+ "app/concepts/api/v1/thing/representer/show.rb",
169
+ "app/concepts/api/v1/thing/operation/create.rb",
170
+ "app/concepts/api/v1/thing/operation/index.rb",
171
+ "app/concepts/api/v1/thing/operation/update.rb"
172
+ ]
173
+ ```
29
174
 
30
175
  ## Installation
31
176
 
@@ -35,55 +180,60 @@ Add this line to your application's Gemfile:
35
180
  gem 'trailblazer-loader'
36
181
  ```
37
182
 
183
+ You do not need this step should you use one of the following binding gems.
184
+
185
+ * [trailblazer-rails](https://github.com/trailblazer/trailblazer-rails)
186
+ * [trailblazer-grape](https://github.com/trailblazer/trailblazer-grape)
187
+ * [trailblazer-roda](https://github.com/trailblazer/trailblazer-roda)
188
+ * [trailblazer-lotus](https://github.com/trailblazer/trailblazer-lotus)
189
+
38
190
  ## API
39
191
 
40
192
  ```ruby
41
- Trailblazer::Loader.new.(Rails.app.root) { |file| require_dependency(file) }
193
+ Trailblazer::Loader.new.() { |file| require_dependency(File.join(Rails.app.root, file)) }
42
194
  ```
43
195
 
196
+ `:concepts_root`
197
+
198
+ ## Mixing
199
+
200
+ Note that you're free to mix these styles the way it feels right for your project.
44
201
 
45
- ## File Layout
202
+ For example, you can have compound files and explicit layout in one concept.
46
203
 
47
204
  ```
48
205
  app
49
206
  ├── concepts
50
- ├── comment
51
- ├── cell.rb [optional]
52
- ├── contract.rb [optional]
53
- │ ├── operation.rb
54
- ├── policy.rb [optional]
55
- │ │ ├── representer.rb [optional]
56
- │ │ ├── views
57
- │ │ │ ├── show.haml
58
- │ │ │ ├── list.haml
59
- │ │ │ ├── comment.css.sass
207
+    ├── comment
208
+       ├── contract.rb - compound vs.
209
+       ├── operation - explicit directory
210
+       │ ├── create.rb
211
+       │ └── update.rb
60
212
  ```
61
213
 
62
- ### Alternative "explicit" layout
214
+ ## Namespacing Operations
63
215
 
64
- This works for cell, contract, operation, policy, representer and twin.
216
+ Normally, operations in Trailblazer use the concept's namespace, e.g. `Comment::Create`, even though they can sit in an explicit file.
65
217
 
66
218
  ```
67
219
  app
68
220
  ├── concepts
69
- ├── comment
70
- ├── operation
71
- │ ├── create.rb
72
- ├── update.rb
221
+    ├── comment
222
+       ├── operation - explicit directory
223
+       │ ├── create.rb - contains Comment::Create
224
+       └── update.rb
73
225
  ```
74
226
 
75
- ### Nested
227
+ You are free to namespace your operations, if you like that better.
76
228
 
229
+ ```ruby
230
+ module Comment::Operation
231
+ class Create < Trailblazer::Operation
77
232
  ```
78
- app
79
- ├── concepts
80
- │ ├── comment
81
- │ │ ├── cell
82
- │ │ │ ├── single
83
- │ │ │ │ ├── homepage.rb
84
- │ │ │ │ ├── search.rb
85
- │ │ │ │ ├── views
86
- │ │ │ │ │ ├── homepage.haml
87
- │ │ │ │ │ ├── search.haml
88
- │ │ │ ├── collection
89
- ```
233
+
234
+ ## Debugging
235
+
236
+ ## Customizing
237
+
238
+ Trailblazer-loader allows you to inject your own sorting and filtering logic, should you refuse to go mainstream.
239
+
@@ -1,5 +1,6 @@
1
1
  require "trailblazer/loader/version"
2
2
  require "representable/pipeline"
3
+ require "pp"
3
4
 
4
5
  module Trailblazer
5
6
  class Loader
@@ -9,35 +10,55 @@ module Trailblazer
9
10
  # NOTE: i will most probably use call_sheet and dry-container here soon.
10
11
  def call(options={}, &block)
11
12
  options[:concepts_root] ||= "app/concepts/"
13
+ options[:concept_dirs] = concept_dirs
12
14
 
13
15
  pipeline = options[:pipeline] || Representable::Pipeline[
16
+ FindDirectories,
14
17
  FindConcepts,
15
- SortConcepts,
16
- Representable::Collect[ConceptName, ConceptFiles] # per concept.
18
+ # PrintConcepts,
19
+ SortByLevel,
20
+ Representable::Collect[ConceptName, ConceptFiles, SortCreateFirst, SortOperationLast, AddConceptFiles] # per concept.
17
21
  ]
18
22
 
19
23
  if args = options[:insert] # FIXME: this only implements a sub-set.
20
24
  # pipeline = Representable::Pipeline::Insert.(pipeline, *args) # FIXME: implement :before in Pipeline.
21
- pipeline[2].insert(pipeline[2].index(args.last[:before]), args.first)
25
+ pipeline[3].insert(pipeline[3].index(args.last[:before]), args.first)
22
26
  end
23
27
 
24
28
  files = pipeline.([], options).flatten
25
29
 
26
- # require "pp"
27
- # pp files
30
+ pp files
28
31
 
29
32
  load_files(files, &block)
30
33
  end
31
34
 
32
- FindConcepts = ->(input, options) { Dir.glob("#{options[:concepts_root]}**/") }
33
- # lame heuristic, but works for me: sort by nested levels.
35
+ def concept_dirs
36
+ %w{ callback cell contract operation policy representer view }
37
+ end
38
+
39
+ FindDirectories = ->(input, options) { Dir.glob("#{options[:concepts_root]}**/") }
40
+ # Filter out all directories containing /(callback|cell|contract|operation|policy|representer|view)/
41
+ FindConcepts = ->(input, options) { input.shift; input.reject { |dir| dir =~ /(#{options[:concept_dirs].join("|")})/ } }
42
+ PrintConcepts = ->(input, options) { puts " concepts: #{input.inspect}"; input }
43
+
44
+ # lame heuristic, but works for me: sort by directory levels.
34
45
  # app/concepts/comment
35
46
  # app/concepts/api/v1/comment
36
- SortConcepts = ->(input, options) { input.sort { |a, b| a.split("/").size <=> b.split("/").size } }
47
+ SortByLevel = ->(input, options) { input.sort { |a, b| a.split("/").size <=> b.split("/").size } }
37
48
  # Extract concept name from path, e.g. /api/v1/comment.
38
49
  ConceptName = ->(input, options) { options[:name] = input.sub(options[:concepts_root], "").chomp("/"); [] }
39
50
  # Find all .rb files in one particular concept directory, e.g. as in /concepts/comment/*.rb.
40
- ConceptFiles = ->(input, options) { input += Dir.glob("#{options[:concepts_root]}#{options[:name]}/*.rb") }
51
+ ConceptFiles = ->(input, options) do
52
+ Dir.glob("#{options[:concepts_root]}#{options[:name]}/*.rb") + # .rb files directly in this concept.
53
+ Dir.glob("#{options[:concepts_root]}#{options[:name]}/*/*.rb"). # .rb in :concept/operation/*.rb
54
+ find_all { |file| file =~ /(#{options[:concept_dirs].join("|")})/ } # but only those, no sub-concepts!
55
+ end
56
+
57
+ # operation files should be loaded after callbacks, policies, and the like: [callback.rb, contract.rb, policy.rb, operation.rb]
58
+ SortOperationLast = ->(input, options) { input.sort { |a, b| (a =~ /operation/ && b !~ /operation/) ? 1 : -1 } }
59
+ SortCreateFirst = ->(input, options) { input.sort }
60
+ AddConceptFiles = ->(input, options) { input }
61
+
41
62
 
42
63
  private
43
64
 
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Loader
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-loader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-16 00:00:00.000000000 Z
11
+ date: 2015-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler