pipehat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +10 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +184 -0
  9. data/Rakefile +10 -0
  10. data/bin/console +10 -0
  11. data/bin/setup +8 -0
  12. data/lib/pipehat.rb +22 -0
  13. data/lib/pipehat/component/base.rb +30 -0
  14. data/lib/pipehat/define_type.rb +68 -0
  15. data/lib/pipehat/field/base.rb +59 -0
  16. data/lib/pipehat/message.rb +32 -0
  17. data/lib/pipehat/node.rb +53 -0
  18. data/lib/pipehat/parser.rb +149 -0
  19. data/lib/pipehat/repeat/base.rb +31 -0
  20. data/lib/pipehat/segment/aip.rb +21 -0
  21. data/lib/pipehat/segment/ais.rb +21 -0
  22. data/lib/pipehat/segment/al1.rb +15 -0
  23. data/lib/pipehat/segment/base.rb +124 -0
  24. data/lib/pipehat/segment/dg1.rb +30 -0
  25. data/lib/pipehat/segment/err.rb +21 -0
  26. data/lib/pipehat/segment/evn.rb +16 -0
  27. data/lib/pipehat/segment/in1.rb +64 -0
  28. data/lib/pipehat/segment/msa.rb +17 -0
  29. data/lib/pipehat/segment/msh.rb +52 -0
  30. data/lib/pipehat/segment/obr.rb +63 -0
  31. data/lib/pipehat/segment/obx.rb +37 -0
  32. data/lib/pipehat/segment/orc.rb +43 -0
  33. data/lib/pipehat/segment/pid.rb +49 -0
  34. data/lib/pipehat/segment/prd.rb +23 -0
  35. data/lib/pipehat/segment/pv1.rb +63 -0
  36. data/lib/pipehat/segment/rf1.rb +34 -0
  37. data/lib/pipehat/segment/rgs.rb +12 -0
  38. data/lib/pipehat/segment/sch.rb +36 -0
  39. data/lib/pipehat/subcomponent/base.rb +27 -0
  40. data/lib/pipehat/types/aui.rb +8 -0
  41. data/lib/pipehat/types/ce.rb +11 -0
  42. data/lib/pipehat/types/cne.rb +27 -0
  43. data/lib/pipehat/types/cnn.rb +16 -0
  44. data/lib/pipehat/types/cp.rb +11 -0
  45. data/lib/pipehat/types/cq.rb +7 -0
  46. data/lib/pipehat/types/cwe.rb +27 -0
  47. data/lib/pipehat/types/cx.rb +17 -0
  48. data/lib/pipehat/types/dld.rb +7 -0
  49. data/lib/pipehat/types/dt.rb +4 -0
  50. data/lib/pipehat/types/dtm.rb +4 -0
  51. data/lib/pipehat/types/ei.rb +9 -0
  52. data/lib/pipehat/types/eip.rb +7 -0
  53. data/lib/pipehat/types/erl.rb +11 -0
  54. data/lib/pipehat/types/fn.rb +10 -0
  55. data/lib/pipehat/types/hd.rb +8 -0
  56. data/lib/pipehat/types/id.rb +4 -0
  57. data/lib/pipehat/types/is.rb +4 -0
  58. data/lib/pipehat/types/mo.rb +7 -0
  59. data/lib/pipehat/types/moc.rb +7 -0
  60. data/lib/pipehat/types/msg.rb +8 -0
  61. data/lib/pipehat/types/ndl.rb +16 -0
  62. data/lib/pipehat/types/nm.rb +4 -0
  63. data/lib/pipehat/types/pl.rb +16 -0
  64. data/lib/pipehat/types/pln.rb +9 -0
  65. data/lib/pipehat/types/prl.rb +8 -0
  66. data/lib/pipehat/types/pt.rb +7 -0
  67. data/lib/pipehat/types/rc.rb +7 -0
  68. data/lib/pipehat/types/sad.rb +8 -0
  69. data/lib/pipehat/types/si.rb +4 -0
  70. data/lib/pipehat/types/snm.rb +4 -0
  71. data/lib/pipehat/types/st.rb +4 -0
  72. data/lib/pipehat/types/ts.rb +7 -0
  73. data/lib/pipehat/types/tx.rb +4 -0
  74. data/lib/pipehat/types/varies.rb +28 -0
  75. data/lib/pipehat/types/vid.rb +8 -0
  76. data/lib/pipehat/types/xad.rb +28 -0
  77. data/lib/pipehat/types/xcn.rb +30 -0
  78. data/lib/pipehat/types/xon.rb +15 -0
  79. data/lib/pipehat/types/xpn.rb +20 -0
  80. data/lib/pipehat/types/xtn.rb +22 -0
  81. data/lib/pipehat/version.rb +3 -0
  82. data/pipehat.gemspec +27 -0
  83. metadata +127 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 321edea28b62f76b4ea8686bacea94d759caee4f0d9f43a581c01e373c0aacdb
4
+ data.tar.gz: 29ce16e9504f7f649eb78917842372157c21a8bff777597b4f3169194a5e966d
5
+ SHA512:
6
+ metadata.gz: c51d419897f44c88f32d773a8f1536a6e026ebcb29acd56cb428b12c407e915980f52bdd1a6a886bfdaf92a712c76074438831ee844818eb020e544826581720
7
+ data.tar.gz: 69a196afd98264821be5906909e10972453ff64aab3f202c88ddfdbb3cd1e51240e7aba6041ca020fc95e46db21d569cd797d2e9d3c25e0990cfb99f807cb935
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
@@ -0,0 +1,18 @@
1
+ ---
2
+ AllCops:
3
+ TargetRubyVersion: 2.4
4
+
5
+ Style/StringLiterals:
6
+ EnforcedStyle: double_quotes
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
9
+
10
+ Metrics/LineLength:
11
+ Max: 120
12
+
13
+ Metrics/AbcSize:
14
+ Exclude:
15
+ - "**/test/*_test.rb"
16
+ Metrics/MethodLength:
17
+ Exclude:
18
+ - "**/test/*_test.rb"
@@ -0,0 +1,10 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.3
6
+ - 2.4
7
+ - 2.5
8
+ - 2.6
9
+ - 2.7
10
+ before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at tim@catminion.net. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in pipehat.gemspec
4
+ gemspec
5
+
6
+ gem "minitest", "~> 5.0"
7
+ gem "pry"
8
+ gem "rake", "~> 12.0"
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Tim Peters
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,184 @@
1
+ # Pipehat [![Build Status](https://travis-ci.org/timocp/pipehat.svg?branch=master)](https://travis-ci.org/timocp/pipehat)
2
+
3
+ Pipehat is a Ruby gem for reading and writing HL7 v.2.x messages.
4
+
5
+ This library is experimental - the API is incomplete and may change, and it does
6
+ not support a lot of segment and data types yet.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'pipehat'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install pipehat
23
+
24
+ ## Usage
25
+
26
+ Pipehat is aware of the type of each HL7v2 field. This allows each field,
27
+ component and sub-component to be accessed by name.
28
+
29
+ ### Parsing HL7
30
+
31
+ Suppose the following simple example of an (incomplete) admission message
32
+ and a PID message with 2 patient IDs (showing a repeating field and
33
+ subcomponents).
34
+
35
+ ```ruby
36
+ # Parse a message from a string
37
+ hl7 = "MSH|^~\&|||||20200927212805+0930||ADT^A01^ADT_A01|1234567890|D|2.5\r" \
38
+ "PID||XXX|1234^^^System1&&L~ABC^^^System2&&L||Family^Given"
39
+ msg = Pipehat::Message.new(hl7)
40
+ ```
41
+
42
+ Messages are essentially an array of segments. Individual segments can be
43
+ accessed using `#segments`. The optional parameter will filter by segment
44
+ type. This method returns an Enumerator.
45
+
46
+ ```ruby
47
+ # Example, pick the first PID segment
48
+ pid = msg.segments(:PID).first
49
+ ```
50
+
51
+ Once a segment is accessed, fields, repeats, components and sub-components
52
+ can be accessed by name. Note that these methods return a subclass of
53
+ Pipehat::Node, which is an index into the segment. To retreive the string
54
+ value at (or under) that position, call `@to_s` on the node.
55
+
56
+ ```ruby
57
+ # Access the value of PID-2 (deprecated patient ID)
58
+ pid.patient_id.to_s #=> "XXX"
59
+
60
+ # Access the first repeat in PID-3 (patient ID list)
61
+ pid.patient_identifier_list.first.to_s #=> "1234"
62
+
63
+ # Which is equivalent to accessing the first component of the field, which
64
+ # can also be accessed by name as it is known to be a CX type:
65
+ pid.patient_identifier_list.first.id_number.to_s #=> "1234"
66
+
67
+ # The 4th component of PID-3 is a composite type (HD), so its parts can
68
+ # also be accessed individually:
69
+ pid.patient_identifier_list.first.assigning_authority.namespace_id.to_s #=> "System1"
70
+ pid.patient_identifier_list.first.assigning_authority.universal_id_type.to_s #=> "L"
71
+
72
+ # Field repetitions can be ignored - in this case, Pipehat will assume you
73
+ # are referring to the first repeat.
74
+ pid.patient_identifier_list.assigning_authority.namespace_id.to_s #=> "System1"
75
+
76
+ # Access the second repeat in PID-3. Indexes into repeats are 1-based, for
77
+ # consistency with other HL7 positional indexing.
78
+ pid.patient_identifier_list[2].to_s #=> "ABC"
79
+ pid.patient_identifier_list[2].id_number.to_s #=> "ABC"
80
+ pid.patient_identifier_list[2].assigning_authority.namespace_id.to_s #=> "System2"
81
+ ```
82
+
83
+ Values can also be accessed positionally by index instead of name. This could
84
+ be useful to access values:
85
+
86
+ * from a segment type this library doesn't understand
87
+ * that have additional components in newer versions of HL7 which this library doesn't understand
88
+ * that are invalid (eg, extra components or subcomponents)
89
+
90
+ ```ruby
91
+ # Equivalent to the last example in the previous section
92
+ pid.field(3).repeat(2).component(1).subcomponent(1) #=> "System2"
93
+
94
+ # Again, repetitions can be ignored if you just want to read from the first
95
+ pid.field(3).component(1).subcomponent(1) #=> "System1"
96
+ ```
97
+
98
+ The method names way of accessing segments can be more verbose but is also
99
+ more readable and self-documenting.
100
+
101
+ ### Generating HL7
102
+
103
+ Example to generate a message the same as the above example. Note that the
104
+ field separator and encoding characters do not have to be provided, they are
105
+ initialized automatically.
106
+
107
+ ```ruby
108
+ msg = Pipehat::Message.new
109
+ msg << Pipehat::Segment::MSH.new.tap do |msh|
110
+ msh.date_time_of_message = Time.now.strftime("%Y%m%d%H%M%S%z")
111
+ msh.message_type.message_code = "ADT"
112
+ msh.message_type.trigger_event = "A01"
113
+ msh.message_type.message_structure = "ADT_A01"
114
+ msh.message_control_id = "1234567890"
115
+ msh.processing_id = "D"
116
+ msh.version_id = "2.5"
117
+ end
118
+ msg << Pipehat::Segment::PID.new.tap do |pid|
119
+ pid.patient_id = "XXX"
120
+ pid.patient_name.family_name = "Family"
121
+ pid.patient_name.given_name = "Given"
122
+
123
+ # PID-3 first repeat (since no repeat is mentioned)
124
+ pid.patient_identifier_list.id_number = "1234"
125
+ pid.patient_identifier_list.assigning_authority.namespace_id = "System1"
126
+ pid.patient_identifier_list.assigning_authority.universal_id_type = "L"
127
+ # PID-3 second repeat
128
+ pid.patient_identifier_list[2].id_number = "ABC"
129
+ pid.patient_identifier_list[2].assigning_authority.namespace_id = "System2"
130
+ pid.patient_identifier_list[2].assigning_authority.universal_id_type = "L"
131
+ end
132
+ msg.to_hl7 #=> "MSH|^~\\&|||||20200927212805+0930||ADT^A01^ADT_A01|1234567890|D|2.5\r..."
133
+ ```
134
+
135
+ The indexed based access looks slightly different due to ruby syntax; the `set`
136
+ method must be called on the node object. `set` can be called at any level
137
+ from field to subcomponent and it will replace all current values.
138
+
139
+ ```ruby
140
+ pid = Pipehat::Segment::PID.new
141
+ pid.field(3).repeat(2).component(4).set("System2")
142
+ pid.field(3).repeat(2).component(4).subcomponent(3).set("L")
143
+ # is equivalent to:
144
+ # pid.patient_identifier_list[2].assigning_authority = "System2"
145
+ # pid.patient_identifier_list[2].assigning_authority.universal_id_type = "L"
146
+
147
+ pid.to_hl7 #=> "PID|||~^^^System2&&L"
148
+ ```
149
+
150
+ ### HL7 Escaping
151
+
152
+ Values returned by the `to_s` methods have HL7 escape codes automatically
153
+ unescaped. Assigning a value will automatically be escaped. The unescaped
154
+ version can be read with `unescaped`.
155
+
156
+ ```ruby
157
+ pid = Pipehat::Segment::PID.new("PID||123\\R\\456")
158
+ pid.patient_id.to_s #=> "123~456"
159
+ pid.patient_id.unescaped #=> "123\\R\\456"
160
+ pid.patient_identifier_list = "A & B"
161
+ pid.to_hl7 #=> "PID||123\\R\\456|A \\T\\ B"
162
+ ```
163
+
164
+ Note there is currently no way to set a string without the escaping (this will
165
+ be needed when writing, for example, HL7 Formatted Text).
166
+
167
+ ## Development
168
+
169
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
170
+
171
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
172
+
173
+ ## Contributing
174
+
175
+ Bug reports and pull requests are welcome on GitHub at https://github.com/timocp/pipehat. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/timocp/pipehat/blob/master/CODE_OF_CONDUCT.md).
176
+
177
+
178
+ ## License
179
+
180
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
181
+
182
+ ## Code of Conduct
183
+
184
+ Everyone interacting in the Pipehat project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/timocp/pipehat/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pry"
5
+ require "pipehat"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ require "pipehat/version"
2
+
3
+ module Pipehat
4
+ class Error < StandardError; end
5
+ end
6
+
7
+ require "pipehat/parser"
8
+ require "pipehat/segment/base"
9
+ require "pipehat/node"
10
+ require "pipehat/field/base"
11
+ require "pipehat/repeat/base"
12
+ require "pipehat/component/base"
13
+ require "pipehat/subcomponent/base"
14
+ require "pipehat/define_type"
15
+ require "pipehat/message"
16
+
17
+ Dir[File.join(__dir__, "pipehat", "types", "*.rb")].sort.each { |file| require file }
18
+ Dir[File.join(__dir__, "pipehat", "segment", "*.rb")].sort.each { |file| require file }
19
+
20
+ ObjectSpace.each_object(Pipehat::Segment::Base.singleton_class).each do |segment_class|
21
+ segment_class.field_names.freeze
22
+ end
@@ -0,0 +1,30 @@
1
+ module Pipehat
2
+ module Component
3
+ class Base < Pipehat::Node
4
+ def initialize(segment, fnum, rnum, cnum)
5
+ @segment = segment
6
+ @fnum = fnum
7
+ @rnum = rnum
8
+ @cnum = cnum
9
+ end
10
+
11
+ attr_reader :segment, :fnum, :rnum, :cnum
12
+
13
+ def subcomponent(snum, type = Pipehat::Subcomponent::Base)
14
+ type.new(segment, fnum, rnum, cnum, snum)
15
+ end
16
+
17
+ def set(value)
18
+ segment.set_component(fnum, rnum, cnum, value)
19
+ end
20
+
21
+ def to_hl7
22
+ (segment.tree(fnum, rnum, cnum) || []).join(parser.subcomponent_sep)
23
+ end
24
+
25
+ def inspect
26
+ inspect_node(fnum, rnum, cnum)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pipehat
4
+ class TypeDefinitionProxy
5
+ def initialize(fclass, rclass, cclass)
6
+ @fclass = fclass
7
+ @rclass = rclass
8
+ @cclass = cclass
9
+ @count = 0
10
+ end
11
+
12
+ def add_component(name, type)
13
+ setter = "#{name}=".to_sym
14
+ count = @count += 1
15
+
16
+ @fclass.component_names << name
17
+ @rclass.component_names << name
18
+ @cclass.component_names << name
19
+
20
+ # component for a field class is delegated to the first repeat and
21
+ # returns a Component
22
+ @fclass.send(:define_method, name) do
23
+ first.send(name)
24
+ end
25
+
26
+ @fclass.send(:define_method, setter) do |value|
27
+ first.send(setter, value)
28
+ end
29
+
30
+ # component in a repeat class returns a Component
31
+ @rclass.send(:define_method, name) do
32
+ component(count, Object.const_get("Pipehat::Component::#{type}"))
33
+ end
34
+
35
+ @rclass.send(:define_method, setter) do |value|
36
+ send(name).set(value)
37
+ end
38
+
39
+ # component in a component class returns a Subcomponent
40
+ @cclass.send(:define_method, name) do
41
+ subcomponent(count, Object.const_get("Pipehat::Subcomponent::#{type}"))
42
+ end
43
+
44
+ @cclass.send(:define_method, setter) do |value|
45
+ send(name).set(value)
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.define_type(type, &block)
51
+ # define classes at each level
52
+ fclass = Pipehat::Field.const_set(type, Class.new(Pipehat::Field::Base))
53
+ rclass = Pipehat::Repeat.const_set(type, Class.new(Pipehat::Repeat::Base))
54
+ cclass = Pipehat::Component.const_set(type, Class.new(Pipehat::Component::Base))
55
+
56
+ if block_given?
57
+ # composite type
58
+ TypeDefinitionProxy.new(fclass, rclass, cclass).instance_eval(&block)
59
+ fclass.component_names.freeze
60
+ rclass.component_names.freeze
61
+ cclass.component_names.freeze
62
+ else
63
+ # primitive type
64
+ sclass = Pipehat::Subcomponent.const_set(type, Class.new(Pipehat::Subcomponent::Base))
65
+ sclass.component_names.freeze
66
+ end
67
+ end
68
+ end