praxis 0.21 → 2.0.pre.3

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -15
  3. data/CHANGELOG.md +328 -299
  4. data/CONTRIBUTING.md +4 -4
  5. data/README.md +11 -9
  6. data/lib/api_browser/app/js/directives/attribute_table.js +2 -1
  7. data/lib/api_browser/app/js/directives/conditional_requirements.js +13 -0
  8. data/lib/api_browser/app/js/directives/type_placeholder.js +10 -1
  9. data/lib/api_browser/app/js/factories/normalize_attributes.js +4 -2
  10. data/lib/api_browser/app/js/factories/template_for.js +5 -2
  11. data/lib/api_browser/app/js/filters/has_requirement.js +14 -0
  12. data/lib/api_browser/app/js/filters/tag_requirement.js +13 -0
  13. data/lib/api_browser/app/sass/praxis.scss +11 -0
  14. data/lib/api_browser/app/views/action.html +2 -2
  15. data/lib/api_browser/app/views/directives/attribute_description/member_options.html +2 -2
  16. data/lib/api_browser/app/views/directives/attribute_table.html +1 -1
  17. data/lib/api_browser/app/views/type.html +1 -1
  18. data/lib/api_browser/app/views/type/details.html +2 -2
  19. data/lib/api_browser/app/views/types/embedded/array.html +2 -0
  20. data/lib/api_browser/app/views/types/embedded/default.html +3 -1
  21. data/lib/api_browser/app/views/types/embedded/requirements.html +6 -0
  22. data/lib/api_browser/app/views/types/embedded/single_req.html +9 -0
  23. data/lib/api_browser/app/views/types/embedded/struct.html +14 -2
  24. data/lib/api_browser/app/views/types/standalone/array.html +1 -1
  25. data/lib/api_browser/app/views/types/standalone/struct.html +2 -1
  26. data/lib/api_browser/package.json +1 -1
  27. data/lib/praxis.rb +9 -3
  28. data/lib/praxis/action_definition.rb +1 -1
  29. data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
  30. data/lib/praxis/application.rb +1 -9
  31. data/lib/praxis/bootloader.rb +1 -4
  32. data/lib/praxis/config.rb +1 -1
  33. data/lib/praxis/dispatcher.rb +10 -6
  34. data/lib/praxis/docs/generator.rb +2 -1
  35. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +180 -0
  36. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +273 -0
  37. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
  38. data/lib/praxis/extensions/field_selection.rb +1 -9
  39. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +51 -0
  40. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +61 -0
  41. data/lib/praxis/extensions/rails_compat.rb +2 -0
  42. data/lib/praxis/extensions/rails_compat/request_methods.rb +19 -0
  43. data/lib/praxis/handlers/xml.rb +1 -1
  44. data/lib/praxis/mapper/active_model_compat.rb +98 -0
  45. data/lib/praxis/mapper/resource.rb +242 -0
  46. data/lib/praxis/mapper/selector_generator.rb +149 -0
  47. data/lib/praxis/mapper/sequel_compat.rb +76 -0
  48. data/lib/praxis/media_type_identifier.rb +2 -1
  49. data/lib/praxis/middleware_app.rb +20 -2
  50. data/lib/praxis/multipart/parser.rb +14 -2
  51. data/lib/praxis/notifications.rb +1 -1
  52. data/lib/praxis/plugins/mapper_plugin.rb +64 -0
  53. data/lib/praxis/plugins/rails_plugin.rb +104 -0
  54. data/lib/praxis/request.rb +7 -1
  55. data/lib/praxis/request_superclassing.rb +11 -0
  56. data/lib/praxis/resource_definition.rb +5 -5
  57. data/lib/praxis/response.rb +1 -1
  58. data/lib/praxis/route.rb +1 -1
  59. data/lib/praxis/routing_config.rb +1 -1
  60. data/lib/praxis/trait.rb +1 -1
  61. data/lib/praxis/types/media_type_common.rb +2 -2
  62. data/lib/praxis/types/multipart.rb +1 -1
  63. data/lib/praxis/types/multipart_array.rb +2 -2
  64. data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
  65. data/lib/praxis/version.rb +1 -1
  66. data/praxis.gemspec +14 -13
  67. data/spec/functional_spec.rb +4 -7
  68. data/spec/praxis/action_definition_spec.rb +1 -1
  69. data/spec/praxis/application_spec.rb +1 -1
  70. data/spec/praxis/collection_spec.rb +3 -2
  71. data/spec/praxis/config_spec.rb +2 -2
  72. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +106 -0
  73. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +147 -0
  74. data/spec/praxis/extensions/field_selection/support/spec_resources_active_model.rb +130 -0
  75. data/spec/praxis/extensions/field_selection/support/spec_resources_sequel.rb +106 -0
  76. data/spec/praxis/handlers/xml_spec.rb +2 -2
  77. data/spec/praxis/mapper/resource_spec.rb +169 -0
  78. data/spec/praxis/mapper/selector_generator_spec.rb +293 -0
  79. data/spec/praxis/media_type_spec.rb +0 -10
  80. data/spec/praxis/middleware_app_spec.rb +29 -9
  81. data/spec/praxis/request_stages/action_spec.rb +8 -1
  82. data/spec/praxis/response_definition_spec.rb +7 -4
  83. data/spec/praxis/response_spec.rb +1 -1
  84. data/spec/praxis/responses/internal_server_error_spec.rb +2 -2
  85. data/spec/praxis/responses/validation_error_spec.rb +2 -2
  86. data/spec/praxis/router_spec.rb +1 -1
  87. data/spec/spec_app/app/controllers/instances.rb +1 -1
  88. data/spec/spec_app/config/environment.rb +3 -21
  89. data/spec/spec_helper.rb +11 -15
  90. data/spec/support/be_deep_equal_matcher.rb +39 -0
  91. data/spec/support/spec_resources.rb +124 -0
  92. data/tasks/thor/templates/generator/empty_app/Gemfile +3 -3
  93. metadata +102 -77
  94. data/.ruby-version +0 -1
  95. data/lib/praxis/extensions/mapper_selectors.rb +0 -16
  96. data/lib/praxis/media_type_collection.rb +0 -127
  97. data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
  98. data/lib/praxis/stats.rb +0 -113
  99. data/spec/praxis/media_type_collection_spec.rb +0 -157
  100. data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
  101. data/spec/praxis/stats_spec.rb +0 -9
  102. data/spec/spec_app/app/models/person.rb +0 -3
@@ -1,157 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Praxis::MediaTypeCollection do
4
-
5
- subject!(:media_type_collection) do
6
- silence_warnings do
7
- klass = Class.new(Praxis::MediaTypeCollection) do
8
- member_type VolumeSnapshot
9
- description 'A container for a collection of Volumes'
10
- display_name 'Volumes Collection'
11
-
12
- attributes do
13
- attribute :name, String, regexp: /snapshots-(\w+)/
14
- attribute :size, Integer
15
- attribute :href, String
16
- end
17
-
18
- view :link do
19
- attribute :name
20
- attribute :size
21
- attribute :href
22
- end
23
-
24
- member_view :default, using: :default
25
- end
26
-
27
- klass.finalize!
28
- klass
29
- end
30
- end
31
-
32
- context '.member_type' do
33
- its(:member_type){ should be(VolumeSnapshot) }
34
- its(:member_attribute){ should be_kind_of(Attributor::Attribute) }
35
- its('member_attribute.type'){ should be(VolumeSnapshot) }
36
- end
37
-
38
- context '.load' do
39
- context 'with a hash' do
40
- let(:snapshots_data) { {name: 'snapshots', href: '/bob/snapshots' } }
41
- subject(:snapshots) { media_type_collection.load(snapshots_data) }
42
-
43
- its(:name) { should eq(snapshots_data[:name]) }
44
- its(:href) { should eq(snapshots_data[:href]) }
45
-
46
- it 'has no members set' do
47
- expect(snapshots.to_a).to eq([])
48
- end
49
- end
50
-
51
-
52
- context 'loading an array' do
53
- let(:snapshots_data) do
54
- [{id: 1, name: 'snapshot-1'},
55
- {id: 2, name: 'snapshot-2'}]
56
- end
57
-
58
- let(:snapshots) { media_type_collection.load(snapshots_data) }
59
- subject(:members) { snapshots.to_a }
60
-
61
- it 'sets the collection members' do
62
- expect(members).to have(2).items
63
-
64
- expect(members[0].id).to eq(1)
65
- expect(members[0].name).to eq('snapshot-1')
66
- expect(members[1].id).to eq(2)
67
- expect(members[1].name).to eq('snapshot-2')
68
- end
69
-
70
- it 'has no attributes set' do
71
- expect(snapshots.name).to be(nil)
72
- expect(snapshots.size).to be(nil)
73
- expect(snapshots.href).to be(nil)
74
- end
75
-
76
- end
77
- end
78
-
79
- context '#render' do
80
- context 'for standard views' do
81
- let(:snapshots_data) { {name: 'snapshots', href: '/bob/snapshots' } }
82
- let(:snapshots) { media_type_collection.load(snapshots_data) }
83
- subject(:output) { snapshots.render(view: :link) }
84
-
85
- its([:name]) { should eq(snapshots.name)}
86
- its([:size]) { should eq(snapshots.size)}
87
- its([:href]) { should eq(snapshots.href)}
88
- end
89
-
90
- context 'for members' do
91
- let(:snapshots_data) do
92
- [{id: 1, name: 'snapshot-1'},
93
- {id: 2, name: 'snapshot-2'}]
94
- end
95
-
96
- let(:snapshots) { media_type_collection.load(snapshots_data) }
97
-
98
- subject(:output) { media_type_collection.dump(snapshots, view: :default) }
99
-
100
- it { should eq(snapshots.collect(&:render)) }
101
- end
102
-
103
- end
104
-
105
- context '#validate' do
106
-
107
-
108
- context 'with a hash' do
109
- let(:snapshots_data) { {name: 'snapshots-1', href: '/bob/snapshots' } }
110
- subject(:snapshots) { media_type_collection.load(snapshots_data) }
111
-
112
-
113
- it 'validates' do
114
- expect(snapshots.validate).to be_empty
115
- end
116
-
117
- context 'with invalid attributes' do
118
- let(:snapshots_data) { {name: 'notsnapshots', href: '/bob/snapshots' } }
119
- it 'returns the error' do
120
- expect(snapshots.validate).to have(1).item
121
- expect(snapshots.validate[0]).to match(/value \(notsnapshots\) does not match regexp/)
122
- end
123
- end
124
- end
125
-
126
- context 'for an array' do
127
- let(:snapshots_data) do
128
- [{id: 1, name: 'snapshot-1'},
129
- {id: 2, name: 'snapshot-2'}]
130
- end
131
-
132
- subject(:snapshots) { media_type_collection.load(snapshots_data) }
133
-
134
- it 'validates' do
135
- expect(snapshots.validate).to be_empty
136
- end
137
-
138
- context 'with invalid members' do
139
- let(:snapshots_data) do
140
- [{id: 1, name: 'invalid-1'},
141
- {id: 2, name: 'snapshot-2'}]
142
- end
143
-
144
- it 'returns the error' do
145
- expect(snapshots.validate).to have(1).item
146
- expect(snapshots.validate[0]).to match(/value \(invalid-1\) does not match regexp/)
147
- end
148
- end
149
- end
150
- end
151
-
152
- context '#describe' do
153
- subject(:described) { media_type_collection.describe }
154
- its([:description]){ should be(media_type_collection.description)}
155
- its([:display_name]){ should be(media_type_collection.display_name)}
156
- end
157
- end
@@ -1,142 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Praxis::Plugins::PraxisMapperPlugin do
4
-
5
- subject(:plugin) { Praxis::Plugins::PraxisMapperPlugin::Plugin.instance }
6
- let(:config) { plugin.config }
7
- context 'Plugin' do
8
- context 'configuration' do
9
- subject { config }
10
- its(:log_stats) { should eq 'detailed' }
11
- its(:stats_log_level) { should eq :info }
12
- its(:repositories) { should have_key("default") }
13
-
14
- context 'default repository' do
15
- subject(:default) { config.repositories['default'] }
16
- its(['type']) { should eq 'sequel' }
17
- it 'has the right connection settings' do
18
- if RUBY_PLATFORM !~ /java/
19
- expect(subject['connection_settings'].dump).to eq( 'adapter' => 'sqlite','database' => ':memory:' )
20
- else
21
- expect(subject['connection_settings'].dump).to eq( 'adapter' => 'jdbc', 'uri' => 'jdbc:sqlite::memory:' )
22
- end
23
-
24
-
25
- end
26
- end
27
-
28
- end
29
- end
30
-
31
- context 'Request' do
32
-
33
- it 'should have identity_map accessors' do
34
- expect(Praxis::Plugins::PraxisMapperPlugin::Request.instance_methods).to include(:identity_map,:identity_map=)
35
- end
36
-
37
- it 'should have silence_mapper_stats accessors' do
38
- expect(Praxis::Plugins::PraxisMapperPlugin::Request.instance_methods)
39
- .to include(:silence_mapper_stats,:silence_mapper_stats=)
40
- end
41
- end
42
- context 'functional test' do
43
-
44
- def app
45
- Praxis::Application.instance
46
- end
47
-
48
- let(:session) { double("session", valid?: true)}
49
-
50
- around(:each) do |example|
51
- orig_level = Praxis::Application.instance.logger.level
52
- Praxis::Application.instance.logger.level = 2
53
- example.run
54
- Praxis::Application.instance.logger.level = orig_level
55
- end
56
-
57
- context 'with no identity_map set in the request' do
58
- it 'does not log stats' do
59
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:log)
60
- the_body = StringIO.new("{}") # This is a funny, GET request expecting a body
61
- get '/api/clouds/1/instances/2?api_version=1.0', nil, 'rack.input' => the_body,'CONTENT_TYPE' => "application/json", 'global_session' => session
62
-
63
- expect(last_response.status).to eq(200)
64
- end
65
- end
66
- context 'with an identity_map set in the request' do
67
- it 'logs stats' do
68
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:log).
69
- with(kind_of(Praxis::Request),kind_of(Praxis::Mapper::IdentityMap), 'detailed').
70
- and_call_original
71
- the_body = StringIO.new("{}") # This is a funny, GET request expecting a body
72
- get '/api/clouds/1/instances/2?create_identity_map=true&api_version=1.0', nil, 'rack.input' => the_body,'CONTENT_TYPE' => "application/json", 'global_session' => session
73
-
74
- expect(last_response.status).to eq(200)
75
- end
76
- end
77
-
78
- end
79
-
80
- context 'Statistics' do
81
- context '.log' do
82
- let(:queries){ { some: :queries } }
83
- let(:identity_map) { double('identity_map', queries: queries) }
84
- let(:log_stats){ 'detailed' }
85
- let(:request){ double('request', silence_mapper_stats: false ) }
86
-
87
- after do
88
- Praxis::Plugins::PraxisMapperPlugin::Statistics.log(request, identity_map, log_stats)
89
- end
90
-
91
- context 'when the request silences mapper stats' do
92
- let(:request){ double('request', silence_mapper_stats: true ) }
93
- it 'should not log anything' do
94
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
95
- end
96
- end
97
-
98
- context 'without the request silencing mapper stats' do
99
- context 'when log_stats = detailed' do
100
- it 'should call the detailed method' do
101
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:detailed).with(identity_map)
102
- end
103
- end
104
-
105
- context 'when log_stats = short' do
106
- let(:log_stats){ 'short' }
107
- it 'should call the short method' do
108
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:short).with(identity_map)
109
- end
110
- end
111
-
112
- context 'when log_stats = skip' do
113
- let(:log_stats){ 'skip' }
114
-
115
- it 'should not log anything' do
116
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
117
- end
118
- end
119
-
120
- context 'when there is no identity map' do
121
- let(:identity_map) { nil }
122
- it 'should not log anything' do
123
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
124
- end
125
- end
126
-
127
- context 'when no queries are logged in the identity map' do
128
- let(:queries){ {} }
129
- it 'should log a special message' do
130
- expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:to_logger)
131
- .with("No database interactions observed.")
132
- end
133
- end
134
-
135
- end
136
- end
137
-
138
- it 'has specs for testing the detailed log output'
139
- it 'has specs for testing the short log output'
140
- end
141
-
142
- end
@@ -1,9 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Praxis::Stats do
4
-
5
- its(:collector) { should be Harness.collector }
6
- its(:queue) { should be Harness.queue }
7
- its(:config) { should be Harness.config }
8
-
9
- end
@@ -1,3 +0,0 @@
1
- class PersonModel < Praxis::Mapper::Model
2
- table_name 'people'
3
- end