praxis 0.21 → 2.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
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