praxis 0.22.pre.2 → 2.0.pre.1

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -324
  3. data/lib/praxis/action_definition.rb +7 -9
  4. data/lib/praxis/api_definition.rb +27 -44
  5. data/lib/praxis/api_general_info.rb +2 -3
  6. data/lib/praxis/application.rb +14 -141
  7. data/lib/praxis/bootloader.rb +1 -2
  8. data/lib/praxis/bootloader_stages/environment.rb +13 -0
  9. data/lib/praxis/controller.rb +0 -2
  10. data/lib/praxis/dispatcher.rb +4 -6
  11. data/lib/praxis/docs/generator.rb +8 -18
  12. data/lib/praxis/docs/link_builder.rb +1 -1
  13. data/lib/praxis/error_handler.rb +5 -5
  14. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
  15. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
  16. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +16 -18
  17. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +5 -5
  18. data/lib/praxis/extensions/field_selection.rb +1 -12
  19. data/lib/praxis/extensions/rendering.rb +1 -1
  20. data/lib/praxis/file_group.rb +1 -1
  21. data/lib/praxis/handlers/xml.rb +1 -1
  22. data/lib/praxis/mapper/active_model_compat.rb +63 -0
  23. data/lib/praxis/mapper/resource.rb +242 -0
  24. data/lib/praxis/mapper/selector_generator.rb +126 -0
  25. data/lib/praxis/mapper/sequel_compat.rb +37 -0
  26. data/lib/praxis/middleware_app.rb +13 -15
  27. data/lib/praxis/multipart/part.rb +3 -5
  28. data/lib/praxis/plugins/mapper_plugin.rb +50 -0
  29. data/lib/praxis/request.rb +14 -7
  30. data/lib/praxis/request_stages/response.rb +2 -3
  31. data/lib/praxis/resource_definition.rb +10 -14
  32. data/lib/praxis/response.rb +6 -5
  33. data/lib/praxis/response_definition.rb +5 -7
  34. data/lib/praxis/response_template.rb +3 -4
  35. data/lib/praxis/responses/http.rb +36 -0
  36. data/lib/praxis/responses/internal_server_error.rb +12 -3
  37. data/lib/praxis/responses/multipart_ok.rb +11 -4
  38. data/lib/praxis/responses/validation_error.rb +10 -1
  39. data/lib/praxis/router.rb +3 -3
  40. data/lib/praxis/tasks/api_docs.rb +2 -10
  41. data/lib/praxis/tasks/routes.rb +0 -1
  42. data/lib/praxis/version.rb +1 -1
  43. data/lib/praxis.rb +13 -9
  44. data/praxis.gemspec +2 -3
  45. data/spec/functional_spec.rb +0 -1
  46. data/spec/praxis/action_definition_spec.rb +15 -26
  47. data/spec/praxis/api_definition_spec.rb +8 -13
  48. data/spec/praxis/api_general_info_spec.rb +8 -3
  49. data/spec/praxis/application_spec.rb +7 -13
  50. data/spec/praxis/handlers/xml_spec.rb +2 -2
  51. data/spec/praxis/mapper/resource_spec.rb +169 -0
  52. data/spec/praxis/mapper/selector_generator_spec.rb +301 -0
  53. data/spec/praxis/middleware_app_spec.rb +15 -9
  54. data/spec/praxis/request_spec.rb +7 -17
  55. data/spec/praxis/request_stages/validate_spec.rb +1 -1
  56. data/spec/praxis/resource_definition_spec.rb +10 -12
  57. data/spec/praxis/response_definition_spec.rb +5 -22
  58. data/spec/praxis/response_spec.rb +5 -12
  59. data/spec/praxis/responses/internal_server_error_spec.rb +5 -2
  60. data/spec/praxis/router_spec.rb +4 -8
  61. data/spec/spec_app/app/models/person.rb +3 -3
  62. data/spec/spec_app/config/environment.rb +3 -21
  63. data/spec/spec_app/config.ru +6 -1
  64. data/spec/spec_helper.rb +2 -17
  65. data/spec/support/spec_resources.rb +131 -0
  66. metadata +19 -31
  67. data/lib/praxis/extensions/attribute_filtering/query_builder.rb +0 -39
  68. data/lib/praxis/extensions/attribute_filtering.rb +0 -28
  69. data/lib/praxis/extensions/mapper_selectors.rb +0 -16
  70. data/lib/praxis/media_type_collection.rb +0 -127
  71. data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
  72. data/spec/praxis/media_type_collection_spec.rb +0 -157
  73. data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
@@ -21,35 +21,17 @@ Praxis::Application.configure do |application|
21
21
  application.bootloader.use SimpleAuthenticationPlugin, config_file: 'config/authentication.yml'
22
22
  application.bootloader.use AuthorizationPlugin
23
23
 
24
-
25
- adapter_name = 'sqlite'
26
- db_name = ':memory:'
27
- connection_opts = if RUBY_PLATFORM !~ /java/
28
- { adapter: adapter_name , database: db_name }
29
- else
30
- require 'jdbc/sqlite3'
31
- { adapter: 'jdbc', uri: "jdbc:#{adapter_name}:#{db_name}" }
32
- end
33
-
34
- application.bootloader.use Praxis::Plugins::PraxisMapperPlugin, {
35
- config_data: {
36
- repositories: { default: connection_opts },
37
- log_stats: 'detailed'
38
- }
39
- }
40
-
41
24
  # enable "development-mode" options
42
25
  application.config.praxis.validate_responses = true
43
26
  application.config.praxis.validate_response_bodies = true
44
27
  application.config.praxis.show_exceptions = true
45
28
 
46
- # FIXME: until we have a better way to unit test such a feature...
47
29
  # Silly callback code pieces to test that the deferred callbacks work even for sub-stages
48
- application.bootloader.after :app, :models do
49
- PersonModel.identity(:other_id)
30
+ application.bootloader.after :app, :controllers do
31
+ $after_app_controllers = :worked
50
32
  end
51
33
  application.bootloader.after :app do
52
- raise "After sub-stage hooks not working!" unless PersonModel.identities.include? :other_id
34
+ raise "After sub-stage hooks not working!" unless $after_app_controllers == :worked
53
35
  end
54
36
 
55
37
 
@@ -9,4 +9,9 @@ $:.unshift File.expand_path('lib', __dir__)
9
9
 
10
10
  require 'praxis'
11
11
 
12
- run Praxis::Application.new.setup
12
+
13
+ application = Praxis::Application.instance
14
+
15
+ application.setup
16
+
17
+ run application
data/spec/spec_helper.rb CHANGED
@@ -19,12 +19,10 @@ require 'rack/test'
19
19
  require 'rspec/its'
20
20
  require 'rspec/collection_matchers'
21
21
 
22
-
23
22
  Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].each do |file|
24
23
  require file
25
24
  end
26
25
 
27
- APP = Praxis::Application.instance
28
26
  Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
29
27
  require file
30
28
  end
@@ -33,11 +31,9 @@ RSpec.configure do |config|
33
31
  config.include Rack::Test::Methods
34
32
 
35
33
  config.before(:suite) do
34
+ Praxis::Mapper::Resource.finalize!
36
35
  Praxis::Blueprint.caching_enabled = true
37
- APP.setup(root:'spec/spec_app')
38
-
39
- # create the table
40
- setup_database!
36
+ Praxis::Application.instance.setup(root:'spec/spec_app')
41
37
  end
42
38
 
43
39
  config.before(:each) do
@@ -49,17 +45,6 @@ RSpec.configure do |config|
49
45
  config.before(:all) do
50
46
  # disable logging below warn level
51
47
  Praxis::Application.instance.logger.level = 2 # warn
52
- Thread.current[:praxis_instance] = APP
53
48
  end
54
49
  end
55
50
 
56
- # create the test db schema
57
- def setup_database!
58
- mapper = Praxis::Application.instance.plugins[:praxis_mapper]
59
- Sequel.connect(mapper.config.repositories["default"]['connection_settings'].dump) do |db|
60
- db.create_table! :people do
61
- primary_key :id
62
- string :name
63
- end
64
- end
65
- end
@@ -0,0 +1,131 @@
1
+ require 'ostruct'
2
+
3
+ require 'praxis/mapper/active_model_compat'
4
+ class SimpleModel < OpenStruct
5
+ include Praxis::Mapper::ActiveModelCompat
6
+ def self._praxis_associations
7
+ {
8
+ parent: {
9
+ model: ParentModel,
10
+ primary_key: :id,
11
+ type: :many_to_one
12
+ },
13
+ other_model: {
14
+ model: OtherModel,
15
+ primary_key: :id,
16
+ type: :many_to_one
17
+ }
18
+ }
19
+ end
20
+ end
21
+
22
+ class OtherModel < OpenStruct
23
+ include Praxis::Mapper::ActiveModelCompat
24
+ def self._praxis_associations
25
+ {
26
+ }
27
+ end
28
+ end
29
+
30
+ class PersonModel < OpenStruct
31
+ include Praxis::Mapper::ActiveModelCompat
32
+ def self._praxis_associations
33
+ {
34
+ }
35
+ end
36
+ end
37
+
38
+ class ParentModel < OpenStruct
39
+ include Praxis::Mapper::ActiveModelCompat
40
+ def self._praxis_associations
41
+ {
42
+ }
43
+ end
44
+ end
45
+
46
+ class YamlArrayModel < OpenStruct
47
+ include Praxis::Mapper::ActiveModelCompat
48
+ def self._praxis_associations
49
+ {
50
+ parents: {
51
+ model: ParentModel,
52
+ primary_key: :id,
53
+ type: :one_to_many
54
+ }
55
+ }
56
+ end
57
+ end
58
+
59
+ # A set of resource classes for use in specs
60
+ class BaseResource < Praxis::Mapper::Resource
61
+ def href
62
+ base_href = '' # "/api"
63
+ base_href + "/#{self.class.collection_name}/#{self.id}"
64
+ end
65
+
66
+ property :href, dependencies: [:id]
67
+ end
68
+
69
+ # class CompositeIdResource < BaseResource
70
+ # model CompositeIdModel
71
+ # end
72
+
73
+ class OtherResource < BaseResource
74
+ model OtherModel
75
+ end
76
+
77
+ class ParentResource < BaseResource
78
+ model ParentModel
79
+ end
80
+
81
+ class SimpleResource < BaseResource
82
+ model SimpleModel
83
+
84
+ resource_delegate :other_model => [:other_attribute]
85
+
86
+ def other_resource
87
+ self.other_model
88
+ end
89
+
90
+ property :other_resource, dependencies: [:other_model]
91
+
92
+ property :name, dependencies: [:simple_name]
93
+ end
94
+
95
+ # class SimplerResource < BaseResource
96
+ # model SimplerModel
97
+ # end
98
+
99
+ class YamlArrayResource < BaseResource
100
+ model YamlArrayModel
101
+ end
102
+
103
+ class PersonResource < BaseResource
104
+ model PersonModel
105
+
106
+ def href
107
+ "/people/#{self.id}"
108
+ end
109
+
110
+ end
111
+
112
+ # class AddressResource < BaseResource
113
+ # model AddressModel
114
+
115
+
116
+ # def href
117
+ # "/addresses/#{self.id}"
118
+ # end
119
+ # property :href, dependencies: [:id]
120
+
121
+ # def owner_name
122
+ # self.owner.name
123
+ # end
124
+ # property :owner_name, dependencies: ['owner.name']
125
+
126
+ # def resident_count
127
+ # self.residents.size
128
+ # end
129
+ # property :resident_count, dependencies: [:residents]
130
+
131
+ # end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: praxis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.pre.2
4
+ version: 2.0.pre.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep M. Blanquer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-09-17 00:00:00.000000000 Z
12
+ date: 2020-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -73,20 +73,6 @@ dependencies:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
- - !ruby/object:Gem::Dependency
77
- name: praxis-mapper
78
- requirement: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '4.3'
83
- type: :runtime
84
- prerelease: false
85
- version_requirements: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '4.3'
90
76
  - !ruby/object:Gem::Dependency
91
77
  name: praxis-blueprints
92
78
  requirement: !ruby/object:Gem::Requirement
@@ -119,16 +105,16 @@ dependencies:
119
105
  name: thor
120
106
  requirement: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - "~>"
108
+ - - ">="
123
109
  - !ruby/object:Gem::Version
124
- version: '0.18'
110
+ version: '0'
125
111
  type: :runtime
126
112
  prerelease: false
127
113
  version_requirements: !ruby/object:Gem::Requirement
128
114
  requirements:
129
- - - "~>"
115
+ - - ">="
130
116
  - !ruby/object:Gem::Version
131
- version: '0.18'
117
+ version: '0'
132
118
  - !ruby/object:Gem::Dependency
133
119
  name: terminal-table
134
120
  requirement: !ruby/object:Gem::Requirement
@@ -371,16 +357,16 @@ dependencies:
371
357
  name: yard
372
358
  requirement: !ruby/object:Gem::Requirement
373
359
  requirements:
374
- - - "~>"
360
+ - - ">="
375
361
  - !ruby/object:Gem::Version
376
- version: '0'
362
+ version: 0.9.20
377
363
  type: :development
378
364
  prerelease: false
379
365
  version_requirements: !ruby/object:Gem::Requirement
380
366
  requirements:
381
- - - "~>"
367
+ - - ">="
382
368
  - !ruby/object:Gem::Version
383
- version: '0'
369
+ version: 0.9.20
384
370
  - !ruby/object:Gem::Dependency
385
371
  name: coveralls
386
372
  requirement: !ruby/object:Gem::Requirement
@@ -536,16 +522,14 @@ files:
536
522
  - lib/praxis/exceptions/invalid_trait.rb
537
523
  - lib/praxis/exceptions/stage_not_found.rb
538
524
  - lib/praxis/exceptions/validation.rb
539
- - lib/praxis/extensions/attribute_filtering.rb
540
525
  - lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb
541
526
  - lib/praxis/extensions/attribute_filtering/filtering_params.rb
542
- - lib/praxis/extensions/attribute_filtering/query_builder.rb
527
+ - lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb
543
528
  - lib/praxis/extensions/field_expansion.rb
544
529
  - lib/praxis/extensions/field_selection.rb
545
530
  - lib/praxis/extensions/field_selection/active_record_query_selector.rb
546
531
  - lib/praxis/extensions/field_selection/field_selector.rb
547
532
  - lib/praxis/extensions/field_selection/sequel_query_selector.rb
548
- - lib/praxis/extensions/mapper_selectors.rb
549
533
  - lib/praxis/extensions/rails_compat.rb
550
534
  - lib/praxis/extensions/rails_compat/request_methods.rb
551
535
  - lib/praxis/extensions/rendering.rb
@@ -555,8 +539,11 @@ files:
555
539
  - lib/praxis/handlers/www_form.rb
556
540
  - lib/praxis/handlers/xml.rb
557
541
  - lib/praxis/links.rb
542
+ - lib/praxis/mapper/active_model_compat.rb
543
+ - lib/praxis/mapper/resource.rb
544
+ - lib/praxis/mapper/selector_generator.rb
545
+ - lib/praxis/mapper/sequel_compat.rb
558
546
  - lib/praxis/media_type.rb
559
- - lib/praxis/media_type_collection.rb
560
547
  - lib/praxis/media_type_identifier.rb
561
548
  - lib/praxis/middleware_app.rb
562
549
  - lib/praxis/multipart/parser.rb
@@ -564,7 +551,7 @@ files:
564
551
  - lib/praxis/notifications.rb
565
552
  - lib/praxis/plugin.rb
566
553
  - lib/praxis/plugin_concern.rb
567
- - lib/praxis/plugins/praxis_mapper_plugin.rb
554
+ - lib/praxis/plugins/mapper_plugin.rb
568
555
  - lib/praxis/plugins/rails_plugin.rb
569
556
  - lib/praxis/request.rb
570
557
  - lib/praxis/request_stages/action.rb
@@ -629,14 +616,14 @@ files:
629
616
  - spec/praxis/handlers/json_spec.rb
630
617
  - spec/praxis/handlers/xml_spec.rb
631
618
  - spec/praxis/links_spec.rb
632
- - spec/praxis/media_type_collection_spec.rb
619
+ - spec/praxis/mapper/resource_spec.rb
620
+ - spec/praxis/mapper/selector_generator_spec.rb
633
621
  - spec/praxis/media_type_identifier_spec.rb
634
622
  - spec/praxis/media_type_spec.rb
635
623
  - spec/praxis/middleware_app_spec.rb
636
624
  - spec/praxis/multipart/parser_spec.rb
637
625
  - spec/praxis/notifications_spec.rb
638
626
  - spec/praxis/plugin_concern_spec.rb
639
- - spec/praxis/plugins/praxis_mapper_plugin_spec.rb
640
627
  - spec/praxis/request_spec.rb
641
628
  - spec/praxis/request_stages/action_spec.rb
642
629
  - spec/praxis/request_stages/request_stage_spec.rb
@@ -687,6 +674,7 @@ files:
687
674
  - spec/support/spec_complex_authentication_plugin.rb
688
675
  - spec/support/spec_media_types.rb
689
676
  - spec/support/spec_resource_definitions.rb
677
+ - spec/support/spec_resources.rb
690
678
  - spec/support/spec_simple_authentication_plugin.rb
691
679
  - tasks/loader.thor
692
680
  - tasks/thor/app.rb
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative 'active_record_filter_query_builder'
3
-
4
- module Praxis
5
- module Extensions
6
- module QueryBuilder
7
- # To include in a resource object...
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- # TODO: this shouldn't be needed if we incorporate it with the properties of the mapper...
12
- def self.filters_mapping(hash)
13
- @query_builder_class = ActiveRecordFilterQueryBuilder.for(**hash)
14
- end
15
-
16
- def self.query_builder_class
17
- @query_builder_class
18
- end
19
-
20
- def self.craft_query(base_query, filters) # rubocop:disable Metrics/AbcSize
21
- # Assume QueryBuilder
22
- if query_builder_class
23
- unless query_builder_class.ancestors.include?(ActiveRecordFilterQueryBuilder)
24
- raise ArgumentError, ':query_builder_class must a class extending FilterQueryBuilder'
25
- end
26
-
27
- if filters && query_builder_class
28
- base_query = query_builder_class.new(query: base_query, model: model ).build_clause(filters)
29
- end
30
- # puts "FILTERS_QUERY: #{filters_query.sql}"
31
- end
32
-
33
- base_query
34
- end
35
- end
36
-
37
- end
38
- end
39
- end
@@ -1,28 +0,0 @@
1
- require 'praxis/extensions/attribute_filtering/filtering_params'
2
- require 'praxis/extensions/attribute_filtering/query_builder'
3
-
4
- # To include in a controller
5
- module Praxis
6
- module Extensions
7
- module AttributeFiltering
8
- extend ActiveSupport::Concern
9
-
10
- def build_query(base_query) # rubocop:disable Metrics/AbcSize
11
-
12
- domain_model = self.media_type&.domain_model
13
- raise "No domain model defined for #{self.name}. Cannot use the attribute filtering helpers without it" unless domain_model
14
-
15
- filters = request.params.filters if request.params&.respond_to?(:filters)
16
- base_query = domain_model.craft_query( base_query , filters )
17
-
18
- # TODO: add the field selector...and the pagination...and the ordering...
19
- resolved = Praxis::MediaType::FieldResolver.resolve(self.media_type, self.expanded_fields)
20
- base_query = FieldSelection::ActiveRecordQuerySelector.new(ds: base_query, model: domain_model.model,
21
- selectors: identity_map.selectors, resolved: resolved).generate
22
-
23
- # TODO: handle pagination and ordering
24
- base_query
25
- end
26
- end
27
- end
28
- end
@@ -1,16 +0,0 @@
1
- module Praxis
2
- module Extensions
3
- module MapperSelectors
4
- extend ActiveSupport::Concern
5
- include FieldExpansion
6
-
7
- def set_selectors
8
- return unless self.media_type.respond_to?(:domain_model) &&
9
- self.media_type.domain_model < Praxis::Mapper::Resource
10
-
11
- resolved = Praxis::MediaType::FieldResolver.resolve(self.media_type, self.expanded_fields)
12
- identity_map.add_selectors(self.media_type.domain_model, resolved)
13
- end
14
- end
15
- end
16
- end
@@ -1,127 +0,0 @@
1
- module Praxis
2
-
3
- module StructCollection
4
- def self.included(klass)
5
- klass.instance_eval do
6
- include(Enumerable)
7
- end
8
- end
9
-
10
- def _members=(members)
11
- @members = members
12
- end
13
-
14
- def _members
15
- @members || []
16
- end
17
-
18
- def each
19
- _members.each { |member| yield(member) }
20
- end
21
- end
22
-
23
- class MediaTypeCollection < MediaType
24
- include Enumerable
25
-
26
- class << self
27
- attr_accessor :member_attribute
28
- end
29
-
30
- def self.inherited(klass)
31
- warn "DEPRECATION: MediaTypeCollection is deprecated and will be removed by 1.0"
32
- super
33
- end
34
-
35
- def self._finalize!
36
- super
37
-
38
- if const_defined?(:Struct, false)
39
- self::Struct.instance_eval do
40
- include StructCollection
41
- end
42
- end
43
- end
44
-
45
- def self.member_type(type=nil)
46
- return ( @member_attribute ? @member_attribute.type : nil) unless type
47
- raise ArgumentError, "invalid type: #{type.name}" unless type < MediaType
48
-
49
- member_options = {}
50
- @member_attribute = Attributor::Attribute.new type, member_options
51
- end
52
-
53
- def self.example(context=nil, options: {})
54
- result = super
55
-
56
- context = case context
57
- when nil
58
- ["#{self.name}-#{values.object_id.to_s}"]
59
- when ::String
60
- [context]
61
- else
62
- context
63
- end
64
-
65
- members = []
66
- size = rand(3) + 1
67
-
68
- size.times do |i|
69
- subcontext = context + ["at(#{i})"]
70
- members << @member_attribute.example(subcontext)
71
- end
72
-
73
- result.object._members = members
74
- result
75
- end
76
-
77
- def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
78
- if value.kind_of?(String)
79
- value = JSON.parse(value)
80
- end
81
-
82
- case value
83
- when nil, self
84
- value
85
- when Hash
86
- # Need to parse/deserialize first
87
- self.new(self.attribute.load(value,context, **options))
88
- when Array, Praxis::Mapper::ResourceDecorator
89
- object = self.attribute.load({})
90
- object._members = value.collect { |subvalue| @member_attribute.load(subvalue) }
91
- self.new(object)
92
- else
93
- # Just wrap whatever value
94
- self.new(value)
95
- end
96
- end
97
-
98
- def self.describe(shallow = false)
99
- hash = super
100
- hash[:member_attribute] = member_attribute.describe(true)
101
- hash
102
- end
103
-
104
- def self.member_view(name, using: nil)
105
- if using
106
- member_view = self.member_type.view(using)
107
- return self.views[name] = CollectionView.new(name, self.member_type, member_view)
108
- end
109
-
110
- self.views[name]
111
- end
112
-
113
-
114
- def each
115
- @object.each { |member| yield(member) }
116
- end
117
-
118
-
119
- def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
120
- errors = super
121
- self.each_with_object(errors) do |member, errors|
122
- errors.push(*member.validate(context))
123
- end
124
- end
125
-
126
- end
127
- end