gdstruct 0.8.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,18 @@
1
- Copyright (c) 2018 Ulrich Ramminger
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
4
- of this software and associated documentation files (the "Software"), to
5
- deal in the Software without restriction, including without limitation the
6
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
- sell copies of the Software, and to permit persons to whom the Software is
8
- furnished to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
- THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ Copyright (c) 2018-2020 Ulrich Ramminger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -2,11 +2,16 @@ gdstruct - GDS - General Data Structure
2
2
  =======================================
3
3
 
4
4
  A General Data Structure (GDS) is a universal, composable data structure, used to store any kind of data.
5
- This could be for example the definition of configurations, specifications and data sets.
6
- Typical usage is the definition of configurations, specifications and data sets.
7
- The GDS language is a special DSL (domain specific language) for defining general data structures.
8
- It uses a succinct, indentation-sensitive syntax which makes data representation clear and readable.
9
5
  The building blocks for general data structures are hashes and arrays.
6
+ In this definition a General Data Structure (GDS) is either a hash or an array or any composition of nested hashes and arrays.
7
+
8
+ The defined data can be used in the following situations:
9
+ * for the configuration or specification of systems, processes, services, frameworks, objects, methods or functions
10
+ * for seeding a database or any other data model
11
+ * to convert to other data representation formats like JSON, XML, YAML
12
+
13
+ The GDS language is a special DSL (domain specific language) for defining general data structures.
14
+ It uses a succinct, indentation-sensitive syntax which makes data representation clear and readable.
10
15
 
11
16
  Installation
12
17
  ============
@@ -14,7 +19,7 @@ Installation
14
19
  ~~~
15
20
  gem install gdstruct
16
21
  ~~~
17
-
22
+
18
23
  A Short Example
19
24
  ===============
20
25
 
@@ -22,11 +27,11 @@ A Short Example
22
27
  require "gdstruct"
23
28
 
24
29
  h = GDstruct.c( <<-EOS )
25
- a val a
26
- b val b
30
+ key1 value a
31
+ key2 value b
27
32
  EOS
28
33
 
29
- # => h = { a: 'val a', b: 'val b' }
34
+ # => h = { key1: 'value a', key2: 'value b' }
30
35
  ~~~
31
36
 
32
37
  An Introduction
@@ -37,9 +42,12 @@ The GDS language uses two basic symbols (__:__ and __,__) for the creation of ha
37
42
  A colon (__:__) is used to define a hash.
38
43
  A comma (__,__) is used to define an array.
39
44
 
40
- Use indentation with two spaces for the definition of elements and for nested structures.
45
+ Use indentation with two spaces for the definition of elements and for nested structures.
41
46
  Tab characters are not allowed for indentation.
42
47
 
48
+ The default data structure is a hash.
49
+ By default, the key of a key-value pair is always converted into a symbol.
50
+
43
51
  ## Another Example
44
52
 
45
53
  ~~~
@@ -61,86 +69,63 @@ videos
61
69
  preview http://mywebsite.com/video.m4v
62
70
  dimensions
63
71
  height 300
64
- width 400
72
+ width 400
65
73
  ~~~
66
74
 
67
75
  transforms to
68
76
 
69
77
  ~~~
70
- {
71
- :caption => 'foo',
72
- :credit => 'bar',
73
- :images => {
74
- :small => {
75
- :url => 'http://mywebsite.com/image-small.jpg',
76
- :dimensions => {
77
- :height => 500,
78
- :width => 500
79
- }
80
- },
81
- :large => {
82
- :url => 'http://mywebsite.com/image-large.jpg',
83
- :dimensions => {
84
- :height => 500,
85
- :width => 500
86
- }
87
- },
88
- },
89
- :videos => {
90
- :small => {
91
- :preview => 'http://mywebsite.com/video.m4v',
92
- :dimensions => {
93
- :height => 300,
94
- :width => 400
95
- }
96
- }
97
- }
98
- }
99
- ~~~
100
-
101
- ## General Example
102
-
103
- ~~~
104
- ,
105
- v1
106
- : k2 v2
107
- : k31 v31 | k32 v32 | k33 v33
108
- k34 v34
109
- k35 v35 | k36 v36
110
- :
111
- k41 v41
112
- k42 v42
113
- k43,
114
- k44, 1 | 2 | 3
115
- 4 | 5
116
- 6
117
- 7
118
- 8 | 9
119
- :k441 v441
120
- v5
121
-
122
- # => [ 'v1', { k2: 'v2' }, { k31: 'v31', k32: 'v32', k33: 'v33', k34: 'v34', k35: 'v35', k36: 'v36' },
123
- { k41: 'v41', k42: 'v42', k43: [], k44: [1, 2, 3, 4, 5, 6, 7, 8, 9, { k441: 'v441' } ] }, 'v5' ]
78
+ { caption: "foo",
79
+ credit: "bar",
80
+ images: {
81
+ small: {
82
+ url: "http://mywebsite.com/image-small.jpg",
83
+ dimensions: {
84
+ height: 500,
85
+ width: 500 } },
86
+ large: {
87
+ url: "http://mywebsite.com/image-large.jpg",
88
+ dimensions: {
89
+ height: 500,
90
+ width: 500 } } },
91
+ videos: {
92
+ small: {
93
+ preview: "http://mywebsite.com/video.m4v",
94
+ dimensions: {
95
+ height: 300,
96
+ width: 400 } } } }
124
97
  ~~~
125
98
 
126
- ## Motivation and Features
99
+ ## Variables and String Interpolation
127
100
 
128
- * the focus is on the essential data
129
- * structure and hierarchy is expressed using indentation (whitespace-sensitive) - avoids curly braces and square brackets
130
- * uses a succinct and minimalist syntax - tries to avoid unnecessary characters
131
- * in many cases colons, commas and quotes are not necessary
132
- * less text makes data clearer and more readable
133
- * for configuration and specification - replacement for XML, JSON, YAML
134
- * for data/database seeding
135
- * less text and less potential errors using schema definitions
136
- * supports block comments, which could be nested
137
- * allows also the definition of hash and array structures in a kind of restricted classic Ruby like syntax
138
- * provides an alternative for Ruby hash and array definition without using eval(); can be used as a protection against code injection vulnerabilities, e.g. on web servers
101
+ You can define a variable as a placeholder for a basic value.
102
+ In conjunction with variables, there is string interpolation supported.
103
+ The values of variables are substituted into string literals.
139
104
 
105
+ ~~~
106
+ $main_path = /usr/john
107
+ $service_dir = docker-compose-service01
108
+ $app01_dir = application_001
109
+ $app02_dir = application_002
110
+ $app03_dir = application_003
111
+ $service_path = $(main_path)/$(service_dir)
112
+
113
+ config
114
+ app01_path $(main_path)/$(service_dir)/$(app01_dir)
115
+ app02_path $(main_path)/$(service_dir)/$(app02_dir)
116
+ app03_path $(service_path)/$(app03_dir)
117
+ ~~~
118
+ transforms to
119
+ ~~~
120
+ { config: {
121
+ app01_path: "/usr/john/docker-compose-service01/application_001",
122
+ app02_path: "/usr/john/docker-compose-service01/application_002",
123
+ app03_path: "/usr/john/docker-compose-service01/application_003" } }
124
+ ~~~
140
125
 
141
- ## Schema specifiers
126
+ ## Schema Definitions
142
127
 
143
- Schema specifiers can be used to predefine the keys of a hash or subhash.
128
+ Schema specifiers can be used to predefine the keys of a hash or subhash.
144
129
  When you specify the values you no longer need to name the key for each value.
145
130
  This facilitates the input for hashes and prevents typos for keys.
146
131
  In combination with the pipe symbol (__\|__), which allows to define multiple values on a single line,
@@ -148,7 +133,7 @@ this features a slim and clearly arranged, table-like style for data.
148
133
 
149
134
  ~~~
150
135
  @schema country(name,capital,area,population,vehicleRegistrationCode,iso3166code,callingCode)
151
- , @schema country /*
136
+ , @schema country /*
152
137
  name capital area (km^2) population vehicleRegistrationCode iso3166code callingCode
153
138
  ---------------------------------------------------------------------------------------------------------------------------- */
154
139
  : Deutschland | Berlin | 357_385 | 82_521_653 | D | DE | 49
@@ -173,13 +158,153 @@ transforms to
173
158
  { :name=>"Canada", :capital=>"Ottawa", :area=>9984670, :population=>36503097, :vehicleRegistrationCode=>"CDN", :iso3166code=>"CA", :callingCode=>1 },
174
159
  { :name=>"France", :capital=>"Paris", :area=>643801, :population=>66991000, :vehicleRegistrationCode=>"F", :iso3166code=>"FR", :callingCode=>33 },
175
160
  { :name=>"Russia", :capital=>"Moscow", :area=>17075400, :population=>144526636, :vehicleRegistrationCode=>"RUS", :iso3166code=>"RU", :callingCode=>7 }
176
- ]
161
+ ]
162
+ ~~~
163
+
164
+ ## References
165
+
166
+ References can be used to define an alias for a certain sub structure, that means for a nested array or a nested hash.
167
+ Afterwards in the GDS definition, you can use the reference to refer to this sub structure.
168
+
169
+ For the definition of a reference you prefix an identifier (unique name) with an __&__ ampersand.
170
+ For the use of a reference you prefix the same identifier with an __*__ asterisk.
177
171
  ~~~
172
+ categories , @schema( name )
173
+ &e_bike : E-Bike
174
+ &mountain_bike : Mountain Bike
175
+ &city_bike : City Bike
176
+ &kids_bike : Kids Bike
177
+
178
+ bikes , @schema( name, frame_size, wheel_diameter, weight, category ) /*
179
+ ----------------------------------------------------------------------------------------
180
+ name frame size ["] wheel diameter ["] weight [kg] category
181
+ ---------------------------------------------------------------------------------------- */
182
+ : Speedstar I | 18 | 27.5 | 23.3 | *city_bike
183
+ : Speedstar I | 20 | 27.5 | 24.4 | *city_bike
184
+ : Speedstar II | 22 | 27.5 | 25.5 | *city_bike
185
+ : Little Pony | 16 | 16 | 5.98 | *kids_bike
186
+ : Rocket | 20 | 20 | 7.13 | *kids_bike
187
+ : Easy Cruiser | 20 | 28 | 29.4 | *e_bike
188
+ : Rough Buffalo | 19 | 26 | 14.7 | *mountain_bike
189
+ ~~~
190
+ transforms to
191
+ ~~~
192
+ { categories: [ { name: "E-Bike" }, { name: "Mountain Bike" }, { name: "City Bike" }, { name: "Kids Bike" } ],
193
+ bikes: [ { name: "Speedstar I", frame_size: 18, wheel_diameter: 27.5, weight: 23.3, category: { name: "City Bike" } },
194
+ { name: "Speedstar I", frame_size: 20, wheel_diameter: 27.5, weight: 24.4, category: { name: "City Bike" } },
195
+ { name: "Speedstar II", frame_size: 22, wheel_diameter: 27.5, weight: 25.5, category: { name: "City Bike" } },
196
+ { name: "Little Pony", frame_size: 16, wheel_diameter: 16, weight: 5.98, category: { name: "Kids Bike" } },
197
+ { name: "Rocket", frame_size: 20, wheel_diameter: 20, weight: 7.13, category: { name: "Kids Bike" } },
198
+ { name: "Easy Cruiser", frame_size: 20, wheel_diameter: 28, weight: 29.4, category: { name: "E-Bike" } },
199
+ { name: "Rough Buffalo", frame_size: 19, wheel_diameter: 26, weight: 14.7, category: { name: "Mountain Bike" } } ] }
200
+ ~~~
201
+
202
+ ### Merging a hash into another - @merge
203
+
204
+ The __@merge__ directive can be used to merge a hash specified with a reference into another hash.
205
+
206
+ ~~~
207
+ $logpath = /var/log/myapp
208
+
209
+ initial_settings &init
210
+ priority 10
211
+ max_size 2000
212
+ max_age 10
213
+
214
+ all_log_files ,
215
+ : @merge *init
216
+ file $(logpath)/logfile_1.log
217
+
218
+ : @merge *init
219
+ file $(logpath)/logfile_2.log
220
+
221
+ : @merge *init
222
+ file $(logpath)/logfile_3.log
223
+ max_age 8 # overwrite an existing hash entry
224
+ ~~~
225
+ transforms to
226
+ ~~~
227
+ { initial_settings: { priority: 10, max_size: 2000, max_age: 10 },
228
+ all_log_files: [ { priority: 10, max_size: 2000, max_age: 10, file: "/var/log/myapp/logfile_1.log" },
229
+ { priority: 10, max_size: 2000, max_age: 10, file: "/var/log/myapp/logfile_2.log" },
230
+ { priority: 10, max_size: 2000, max_age: 8, file: "/var/log/myapp/logfile_3.log" } ] }
231
+ ~~~
232
+
233
+ ### Inserting an array into another - @insert
234
+
235
+ The __@insert__ directive can be used to insert an array specified with a reference into another array.
236
+
237
+ ~~~
238
+ config_files &config_files ,
239
+ Gemfile
240
+ config/application.gdstruct
241
+
242
+ docu_files &docu_files ,
243
+ README.md
244
+ CHANGELOG.md
245
+
246
+ app_files &app_files ,
247
+ appmodel.rb
248
+ appview.rb
249
+ appcontroller.rb
250
+
251
+ complete_file_list ,
252
+ @insert *config_files
253
+ @insert *docu_files
254
+ @insert *app_files
255
+ my_special.rb
256
+ ~~~
257
+ transforms to
258
+ ~~~
259
+ { config_files: [ "Gemfile", "config/application.gdstruct" ],
260
+ docu_files: [ "README.md", "CHANGELOG.md" ],
261
+ app_files: [ "appmodel.rb", "appview.rb", "appcontroller.rb" ],
262
+ complete_file_list: [ "Gemfile", "config/application.gdstruct", "README.md", "CHANGELOG.md",
263
+ "appmodel.rb", "appview.rb", "appcontroller.rb", "my_special.rb" ] }
264
+ ~~~
265
+
266
+ ## Motivation and Features
267
+
268
+ * the focus is on the essential data
269
+ * a language for humans, a human-writable format
270
+ * structure and hierarchy is expressed using indentation (whitespace-sensitive) - avoids curly braces and square brackets
271
+ * uses a succinct and minimalist syntax - tries to avoid unnecessary characters
272
+ * in many cases colons, commas and quotes are not necessary
273
+ * less text makes data clearer and more readable
274
+ * for configuration and specification - replacement for XML, JSON, YAML
275
+ * for data/database seeding
276
+ * less text and less potential errors using schema definitions
277
+ * supports block comments, which could be nested
278
+ * allows also the definition of hash and array structures in a kind of restricted classic Ruby like syntax
279
+ * provides an alternative for Ruby hash and array definition without using eval(); can be used as a protection against code injection vulnerabilities, e.g. on web servers
280
+
281
+ Ruby Gem
282
+ ========
283
+
284
+ You can find it on RubyGems.org:
285
+
286
+ [rubygems.org/gems/gdstruct](https://rubygems.org/gems/gdstruct)
287
+
288
+ <div class="topic-separator"></div>
289
+ <div id="l-source-code" class="fragment-link"></div>
290
+
291
+ Source Code
292
+ ===========
293
+
294
+ You can find the source code on GitHub:
295
+
296
+ [github.com/uliramminger/gdstruct](https://github.com/uliramminger/gdstruct)
178
297
 
179
298
  Further Information
180
299
  ===================
181
300
 
182
- You will find further information here: [urasepandia.de/gds.html](https://urasepandia.de/gds.html)
301
+ You will find detailed information here: [urasepandia.de/gds.html](https://urasepandia.de/gds.html)
302
+
303
+ Use the GDS Converter, play with it and give the GDS language a try: [urasepandia.de/gdsconverter.html](https://urasepandia.de/gdsconverter.html)
304
+ The GDS Converter translates a GDS definition into various data representation formats and languages like Ruby, JSON, Python, XML and YAML.
305
+
306
+ There is a collection of practical examples on GitHub:
307
+ [github.com/uliramminger/gds-examples](https://github.com/uliramminger/gds-examples)
183
308
 
184
309
  Maintainer
185
310
  ==========
@@ -189,6 +314,6 @@ Uli Ramminger <uli@urasepandia.de>
189
314
  Copyright
190
315
  =========
191
316
 
192
- Copyright (c) 2018 Ulrich Ramminger
317
+ Copyright (c) 2018-2020 Ulrich Ramminger
193
318
 
194
319
  See MIT-LICENSE for further details.
@@ -1,34 +1,25 @@
1
- #===============================================================================
2
- # file name : gdstruct.rb
3
- #===============================================================================
4
- =begin - Description
5
- --------------------------------------------------------------------------------
1
+ # gdstruct.rb
6
2
 
7
- You will find further information here: https://urasepandia.de/gds.html
3
+ # You will find further information here: https://urasepandia.de/gds.html
8
4
 
9
- --------------------------------------------------------------------------------
10
- =end
11
- #===============================================================================
12
-
13
- require_relative 'gdstruct/gds_013'
14
-
15
- #===============================================================================
5
+ require_relative 'gdstruct/version'
6
+ require_relative 'gdstruct/lng_gds'
16
7
 
17
8
  class GDstruct
18
-
9
+
19
10
  class << self
20
-
11
+
21
12
  # create a Ruby Hash/Array structure out of a GDS definition string
22
13
  #
23
14
  # @param gds_definition [String] GDS definition
24
- # @option config [true/false] :allow_env (false) allow the @env directive to access environment variables, otherwise the @env directive returns nil
25
- # @option config [Binding] :context (nil) if a binding is set then the @r directive evaluates embedded ruby code, otherwise the @r directive returns nil
15
+ # @option config [true/false] :allow_env (false) allow the @env directive to access environment variables, otherwise the @env directive returns nil
16
+ # @option config [Binding] :context (nil) if a binding is set then the @r directive evaluates embedded ruby code, otherwise the @r directive returns nil
26
17
  #
27
18
  # @return [Hash,Array] hash/array structure
28
19
  #
29
20
  # @example
30
21
  # require 'gdstruct'
31
- #
22
+ #
32
23
  # h = GDstruct.c( <<-EOS )
33
24
  # a value 1
34
25
  # b value 2
@@ -39,21 +30,71 @@ class GDstruct
39
30
  LDLgeneratedLanguage::Gds.parse( gds_definition, config )
40
31
  end
41
32
  alias :c :create
42
-
33
+
43
34
  # create a Ruby Hash/Array structure out of a GDS definition file
44
35
  #
45
36
  # @param file_name [String] file name
46
- # @option config [true/false] :allow_env (false) allow the @env directive to access environment variables, otherwise the @env directive returns nil
47
- # @option config [Binding] :context (nil) if a binding is set then the @r directive evaluates embedded ruby code, otherwise the @r directive returns nil
37
+ # @option config [true/false] :allow_env (false) allow the @env directive to access environment variables, otherwise the @env directive returns nil
38
+ # @option config [Binding] :context (nil) if a binding is set then the @r directive evaluates embedded ruby code, otherwise the @r directive returns nil
48
39
  #
49
40
  # @return [Hash,Array] hash/array structure
50
41
  def create_from_file( file_name, config = {} )
51
42
  LDLgeneratedLanguage::Gds.parse( File.read( file_name ), config )
52
43
  end
53
44
  alias :c_from_file :create_from_file
54
-
45
+
55
46
  end
56
-
57
- end
58
47
 
59
- #===============================================================================
48
+ # create a Ruby Hash/Array structure out of possibly several separated GDS definition strings
49
+ #
50
+ # # file: data.gdstruct
51
+ # &persons persons, @schema person /*
52
+ # firstname lastname yearOfBirth */
53
+ # : John | McArthur | 1987
54
+ # : Berry | Miller | 1976
55
+ #
56
+ # @example
57
+ # require 'gdstruct'
58
+ #
59
+ # creator = GDstruct::Creator.new
60
+ #
61
+ # creator.include( <<-EOS )
62
+ # @schema person( firstname, lastname, yearOfBirth )
63
+ # EOS
64
+ #
65
+ # creator.include_file( 'data.gdstruct' )
66
+ #
67
+ # creator.include( <<-EOS )
68
+ # all
69
+ # mypersons *persons
70
+ # EOS
71
+ #
72
+ # res = creator.create
73
+ #
74
+ # # => res = {:persons=>[{:firstname=>"John", :lastname=>"McArthur", :yearOfBirth=>1987}, {:firstname=>"Berry", :lastname=>"Miller", :yearOfBirth=>1976}],
75
+ # # :all=>{:mypersons=>{:persons=>[{:firstname=>"John", :lastname=>"McArthur", :yearOfBirth=>1987}, {:firstname=>"Berry", :lastname=>"Miller", :yearOfBirth=>1976}]}}}
76
+ class Creator
77
+
78
+ def initialize
79
+ @gdsDefinition = ""
80
+ end
81
+
82
+ def reset
83
+ @gdsDefinition = ""
84
+ end
85
+
86
+ def include( gds_definition )
87
+ @gdsDefinition << gds_definition << "\n"
88
+ end
89
+
90
+ def include_file( file_name )
91
+ @gdsDefinition << File.read( file_name ) << "\n"
92
+ end
93
+
94
+ def create( config = {} )
95
+ LDLgeneratedLanguage::Gds.parse( @gdsDefinition, config )
96
+ end
97
+
98
+ end
99
+
100
+ end