moonrope 2.0.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5af89a4e614e26c289fad12a69304c7a2f293562
4
- data.tar.gz: e2db44de7655b8d4d3aa659fd814b1fdca6af522
3
+ metadata.gz: ec14194988474a3b6e4e0d3e40ffafb64a3fb4c1
4
+ data.tar.gz: 968649b6047917e0713c0510ed32d2c66d4265d9
5
5
  SHA512:
6
- metadata.gz: 90e9f623a0f62e3564fc9ba17850684f263810a04c707ff11765d12522ceef810d8524f8c93b8cc9b3a29e6a72663593552fe6050016c57c7ffe373128969d0c
7
- data.tar.gz: 1d661f3d8acc4352ecc936ccbbf42d18afbefcf128db5b993e0c3edd947b84b360af8b6864e21a974b12c29c44be50e04d233c83a46afe1cce7fbd6c62704e8f
6
+ metadata.gz: dbda03f158543eb581faf08fe4954ce4ac567bf03017e0e33dcef295da04b28d262eaa0038facc3e8c62e84f42fdd79f41580e571ba01a659a335c0ff564a1bf
7
+ data.tar.gz: 64e355f2ef7a89bd545e951070737d2668de5a38c60013b1355c2277a4f0b02318e4d0e7223bd675e2a5364d2d1f9c887b30c3fb3730b9c170c8eddbaf432058
@@ -107,19 +107,21 @@ module Moonrope
107
107
  # Execute a block of code and catch approprite Moonrope errors and return
108
108
  # a result.
109
109
  #
110
- def convert_errors_to_action_result(&block)
110
+ def convert_errors_to_action_result(start_time = nil, &block)
111
111
  begin
112
112
  yield block
113
113
  rescue => exception
114
114
  case exception
115
115
  when Moonrope::Errors::RequestError
116
116
  result = ActionResult.new(self)
117
+ result.time = start_time ? (Time.now - start_time).round(2) : nil
117
118
  result.status = exception.status
118
119
  result.data = exception.data
119
120
  result
120
121
  else
121
122
  if error_block = @controller.base.external_errors[exception.class]
122
123
  result = ActionResult.new(self)
124
+ result.time = start_time ? (Time.now - start_time).round(2) : nil
123
125
  error_block.call(exception, result)
124
126
  result
125
127
  else
@@ -149,14 +151,14 @@ module Moonrope
149
151
  #
150
152
  eval_environment.default_params = self.default_params
151
153
 
152
- convert_errors_to_action_result do
154
+ start_time = Time.now
155
+
156
+ convert_errors_to_action_result(start_time) do
153
157
  #
154
158
  # Validate the parameters
155
159
  #
156
160
  self.validate_parameters(eval_environment.params)
157
161
 
158
- start_time = Time.now
159
-
160
162
  # Run before filters
161
163
  controller.before_actions_for(name).each do |action|
162
164
  eval_environment.instance_eval(&action.block)
@@ -17,6 +17,9 @@ module Moonrope
17
17
  # @return [Symbol] the name of the authenticator
18
18
  attr_reader :name
19
19
 
20
+ # @return [String] the friendly name for the authenticator
21
+ attr_accessor :friendly_name
22
+
20
23
  # @return [String] the description for the authenticator
21
24
  attr_accessor :description
22
25
 
@@ -61,6 +61,23 @@ module Moonrope
61
61
  @dsl.instance_eval(&block) if block_given?
62
62
  end
63
63
 
64
+ #
65
+ # Make a new base based on configuration
66
+ #
67
+ def copy_from(other)
68
+ @environment = other.environment
69
+ @load_directories = other.load_directories
70
+ @on_request = other.on_request
71
+ other.request_error_callbacks.each { |block| self.register_request_error_callback(&block) }
72
+ other.external_errors.each { |error, block| self.register_external_error(error, &block) }
73
+ end
74
+
75
+ def copy
76
+ new_base = self.class.new
77
+ new_base.copy_from(self)
78
+ new_base
79
+ end
80
+
64
81
  #
65
82
  # Reset the whole base to contain no data.
66
83
  #
@@ -6,6 +6,10 @@ module Moonrope
6
6
  @authenticator = authenticator
7
7
  end
8
8
 
9
+ def friendly_name(value)
10
+ @authenticator.friendly_name = value
11
+ end
12
+
9
13
  def description(value)
10
14
  @authenticator.description = value
11
15
  end
@@ -77,6 +77,7 @@ module Moonrope
77
77
  attribute.value = options[:value]
78
78
  attribute.example = options[:eg] || options[:example]
79
79
  attribute.doc = options[:doc]
80
+ attribute.mutation = options[:mutation]
80
81
  attribute.groups = @groups
81
82
  attribute.conditions = @conditions
82
83
  @structure.attributes[type] << attribute
@@ -25,23 +25,6 @@ module Moonrope
25
25
  #
26
26
  def call(env)
27
27
  if env['PATH_INFO'] =~ Moonrope::Request.path_regex
28
-
29
- if @options[:reload_on_each_request]
30
- @base.load
31
- end
32
-
33
- #
34
- # Call the on request block if one has been defined for the base.
35
- #
36
- if @base.on_request.is_a?(Proc)
37
- @base.on_request.call(@base, env)
38
- end
39
-
40
- #
41
- # Create a new request object
42
- #
43
- request = @base.request(env, $1)
44
-
45
28
  #
46
29
  # Set some global headers which are always returned
47
30
  #
@@ -65,6 +48,32 @@ module Moonrope
65
48
  #
66
49
  global_headers['Content-Type'] = 'application/json'
67
50
 
51
+ #
52
+ # Reload if needed
53
+ #
54
+ if @options[:reload_on_each_request]
55
+ base = @base.copy
56
+ begin
57
+ base.load
58
+ rescue => e
59
+ return generate_error_triplet(@base, e, global_headers)
60
+ end
61
+ else
62
+ base = @base
63
+ end
64
+
65
+ #
66
+ # Call the on request block if one has been defined for the base.
67
+ #
68
+ if base.on_request.is_a?(Proc)
69
+ base.on_request.call(base, env)
70
+ end
71
+
72
+ #
73
+ # Create a new request object
74
+ #
75
+ request = base.request(env, $1)
76
+
68
77
  #
69
78
  # Check the request is valid
70
79
  #
@@ -78,30 +87,13 @@ module Moonrope
78
87
  begin
79
88
  result = request.execute
80
89
  json = result.to_json
90
+ Moonrope.logger.info "[#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}] controller=#{request.controller.name} action=#{request.action.name} status=#{result.status} time=#{result.time} ip=#{request.ip} size=#{json.bytesize}"
81
91
  global_headers['Content-Length'] = json.bytesize.to_s
82
92
  [200, global_headers.merge(result.headers), [json]]
83
93
  rescue JSON::ParserError => e
84
94
  [400, global_headers, [{:status => 'invalid-json', :details => e.message}.to_json]]
85
95
  rescue => e
86
- Moonrope.logger.info e.class
87
- Moonrope.logger.info e.message
88
- Moonrope.logger.info e.backtrace.join("\n")
89
-
90
- response = {:status => 'internal-server-error'}
91
-
92
- # Call any request errors which have been registered on the base
93
- @base.request_error_callbacks.each do |callback|
94
- callback.call(request, e)
95
- end
96
-
97
- # If in development, return more details about the exception which was raised.
98
- if @base.environment == 'development'
99
- response[:error] = e.class.to_s
100
- response[:message] = e.message
101
- response[:backtrace] = e.backtrace[0,6]
102
- end
103
-
104
- [500, global_headers, [response.to_json]]
96
+ generate_error_triplet(base, e, global_headers)
105
97
  end
106
98
 
107
99
  else
@@ -113,5 +105,27 @@ module Moonrope
113
105
  end
114
106
  end
115
107
 
108
+ def generate_error_triplet(base, exception, headers = {})
109
+ Moonrope.logger.info exception.class
110
+ Moonrope.logger.info exception.message
111
+ Moonrope.logger.info exception.backtrace.join("\n")
112
+
113
+ response = {:status => 'internal-server-error'}
114
+
115
+ # Call any request errors which have been registered on the base
116
+ base.request_error_callbacks.each do |callback|
117
+ callback.call(request, exception)
118
+ end
119
+
120
+ # If in development, return more details about the exception which was raised.
121
+ if base.environment == 'development'
122
+ response[:error] = exception.class.to_s
123
+ response[:message] = exception.message
124
+ response[:backtrace] = exception.backtrace[0,6]
125
+ end
126
+
127
+ [500, headers, [response.to_json]]
128
+ end
129
+
116
130
  end
117
131
  end
@@ -120,7 +120,7 @@ module Moonrope
120
120
  #
121
121
  def params
122
122
  @params ||= begin
123
- if @env['CONTENT_TYPE'] == 'application/json'
123
+ if @env['CONTENT_TYPE'] && @env['CONTENT_TYPE'] =~ /\Aapplication\/json(;|\z)/i
124
124
  Moonrope::ParamSet.new(rack_request.body.read)
125
125
  else
126
126
  Moonrope::ParamSet.new(rack_request.params['params'])
@@ -85,13 +85,29 @@ module Moonrope
85
85
  end
86
86
  end
87
87
 
88
+ if options[:attributes]
89
+ hash.reject! { |k,v| !options[:attributes].include?(k.to_sym) }
90
+ end
91
+
88
92
  # Add expansions
89
93
  if options[:expansions]
90
94
 
95
+ if options[:expansions].is_a?(Array)
96
+ expansions_to_include = options[:expansions].each_with_object({}) do |expan, hash|
97
+ if expan.is_a?(Symbol) || expan.is_a?(String)
98
+ hash[expan.to_sym] = {}
99
+ elsif expan.is_a?(Hash)
100
+ hash[expan.first.first.to_sym] = expan.first.last
101
+ end
102
+ end
103
+ else
104
+ expansions_to_include = true
105
+ end
106
+
91
107
  # Add structured expansions
92
108
  @attributes[:expansion].each do |attribute|
93
- next if options[:expansions].is_a?(Array) && !options[:expansions].include?(attribute.name.to_sym)
94
- DeepMerge.deep_merge! hash_for_attributes([attribute], object, environment), hash
109
+ next if expansions_to_include.is_a?(Hash) && !expansions_to_include.keys.include?(attribute.name.to_sym)
110
+ DeepMerge.deep_merge! hash_for_attributes([attribute], object, environment, :structure_opts => expansions_to_include.is_a?(Hash) && expansions_to_include[attribute.name.to_sym]), hash
95
111
  end
96
112
 
97
113
  # Add the expansions
@@ -162,7 +178,7 @@ module Moonrope
162
178
  #
163
179
  # Return a returnable hash for a given set of structured fields.
164
180
  #
165
- def hash_for_attributes(attributes, object, environment)
181
+ def hash_for_attributes(attributes, object, environment, value_options = {})
166
182
  return {} unless attributes.is_a?(Array)
167
183
  Hash.new.tap do |hash|
168
184
  attributes.each do |attribute|
@@ -180,9 +196,11 @@ module Moonrope
180
196
  elsif attribute.value
181
197
  value = attribute.value
182
198
  else
183
- value = value_for_attribute(object, environment, attribute)
199
+ value = value_for_attribute(object, environment, attribute, value_options)
184
200
  end
185
201
 
202
+ value = attribute.mutate(value)
203
+
186
204
  if attribute.groups.empty?
187
205
  hash[attribute.name] = value
188
206
  else
@@ -203,7 +221,7 @@ module Moonrope
203
221
  #
204
222
  # Return a value for a structured field.
205
223
  #
206
- def value_for_attribute(object, environment, attribute)
224
+ def value_for_attribute(object, environment, attribute, options = {})
207
225
  if attribute.source_attribute.is_a?(Proc)
208
226
  value = environment.instance_eval(&attribute.source_attribute)
209
227
  else
@@ -214,7 +232,7 @@ module Moonrope
214
232
  # If a structure is required, lookup the desired structure and set the
215
233
  # hash value as appropriate.
216
234
  if structure = self.base.structure(attribute.structure)
217
- structure_opts = attribute.structure_opts || {}
235
+ structure_opts = options[:structure_opts] || attribute.structure_opts || {}
218
236
  if value.respond_to?(:map)
219
237
  value.map do |v|
220
238
  structure.hash(v, structure_opts.merge(:request => environment.request))
@@ -12,6 +12,7 @@ module Moonrope
12
12
  attr_accessor :value
13
13
  attr_accessor :example
14
14
  attr_accessor :doc
15
+ attr_accessor :mutation
15
16
 
16
17
  def initialize(type, name)
17
18
  @type = type
@@ -28,12 +29,35 @@ module Moonrope
28
29
  ([groups] + [name]).flatten.compact.join('.')
29
30
  end
30
31
 
32
+ def mutate(value)
33
+ if mutation
34
+ value ? value.public_send(mutation) : nil
35
+ else
36
+ auto_mutate(value)
37
+ end
38
+ end
39
+
40
+ def auto_mutate(value)
41
+ case value_type
42
+ when :timestamp
43
+ value.is_a?(Time) ? value.to_s : value
44
+ when :unix_timestamp
45
+ value.to_i
46
+ else
47
+ value
48
+ end
49
+ end
50
+
31
51
  def example
32
52
  @example ||= begin
33
53
  if value_type == :timestamp
34
54
  "2016-12-25 09:42:00 +0000"
55
+ elsif value_type == :unix_timestamp
56
+ "1491070507"
35
57
  elsif value_type == :boolean
36
58
  "false"
59
+ elsif value_type == :uuid
60
+ "017dabc1-3f4f-47ab-ab7d-86e2ed0de679"
37
61
  end
38
62
  end
39
63
  end
@@ -1,3 +1,3 @@
1
1
  module Moonrope
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
3
  end
@@ -1,77 +1,77 @@
1
- <p class='tryFormActivate'><a class='tryFormActivate__button' href='#'>Try this request in your browser</a></p>
2
- <form class='tryForm'>
3
- <input type='hidden' name='controller' value='<%= controller.name %>'>
4
- <input type='hidden' name='action' value='<%= action.name %>'>
5
- <div class='tryForm__header'>
6
- <input type='text' id='host' name='host' value='<%= host %>'>
7
- /api/
8
- <input type='text' id='version' name='version' value='v1' class='v'>
9
- /<%= controller.name %>/<%= action.name %>
10
- </div>
1
+ <% unless host.nil? %>
2
+ <p class='tryFormActivate'><a class='tryFormActivate__button' href='#'>Try this request in your browser</a></p>
3
+ <form class='tryForm'>
4
+ <input type='hidden' name='controller' value='<%= controller.name %>'>
5
+ <input type='hidden' name='action' value='<%= action.name %>'>
6
+ <div class='tryForm__header'>
7
+ <input type='text' id='host' name='host' value='<%= host %>'>
8
+ /api/
9
+ <input type='text' id='version' name='version' value='v1' class='v'>
10
+ /<%= controller.name %>/<%= action.name %>
11
+ </div>
11
12
 
12
- <% if action.authenticator_to_use.is_a?(Moonrope::Authenticator) %>
13
- <p class='tryForm__heading'>Headers</p>
14
- <table class='tryForm__table'>
15
- <% for name, options in action.authenticator_to_use.headers %>
16
- <tr>
17
- <td width="50%"><code><%= name %></code></td>
18
- <td width="50%"><input type='text' class='tryForm__tableField headerField' name='<%= name %>'></td>
19
- </tr>
13
+ <% if action.authenticator_to_use.is_a?(Moonrope::Authenticator) %>
14
+ <p class='tryForm__heading'>Headers</p>
15
+ <table class='tryForm__table'>
16
+ <% for name, options in action.authenticator_to_use.headers %>
17
+ <tr>
18
+ <td width="50%"><code><%= name %></code></td>
19
+ <td width="50%"><input type='text' class='tryForm__tableField headerField' name='<%= name %>'></td>
20
+ </tr>
21
+ <% end %>
22
+ </table>
20
23
  <% end %>
21
- </table>
22
- <% end %>
23
24
 
24
- <% unless action.params.empty? %>
25
- <p class='tryForm__heading'>Parameters</p>
26
- <table class='tryForm__table'>
27
- <% for name, param in action.params %>
28
- <tr>
29
- <td width="30%"><code><%= name %></code></td>
30
- <td width="20%"><%= friendly_type param[:type] %></td>
31
- <td width="50%"><input type='text' class='tryForm__tableField paramField' name='<%= name %>' placeholder='<%= param[:default] %>' data-type='<%= param[:type] %>'></td>
32
- </tr>
25
+ <% unless action.params.empty? %>
26
+ <p class='tryForm__heading'>Parameters</p>
27
+ <table class='tryForm__table'>
28
+ <% for name, param in action.params %>
29
+ <tr>
30
+ <td width="30%"><code><%= name %></code></td>
31
+ <td width="20%"><%= friendly_type param[:type] %></td>
32
+ <td width="50%"><input type='text' class='tryForm__tableField paramField' name='<%= name %>' placeholder='<%= param[:default] %>' data-type='<%= param[:type] %>'></td>
33
+ </tr>
34
+ <% end %>
35
+ </table>
33
36
  <% end %>
34
- </table>
35
- <% end %>
36
37
 
37
- <% if action.can_change_full? || action.can_change_expansions? %>
38
- <p class='tryForm__heading'>Structures</p>
39
- <table class='tryForm__table'>
40
- <% if action.can_change_full? %>
41
- <tr>
42
- <td width="50%">Include extended attributes?</td>
43
- <td width="50%">
44
- <div class='tryForm__checkbox'>
45
- <input type='checkbox' class='tryForm__fullAttrs' name='full' id="full_attrs" <% if action.includes_full_attributes? %>checked='checked'<%end%>>
46
- <label for="full_attrs">Yes - include extended attributes</label>
47
- </div>
48
- </td>
49
- </tr>
50
- <% end %>
51
- <% if action.can_change_expansions? %>
52
- <tr>
53
- <td width="50%">Include expansions?</td>
54
- <td width="50%">
55
- <% for expansion in action.available_expansions %>
38
+ <% if action.can_change_full? || action.can_change_expansions? %>
39
+ <p class='tryForm__heading'>Structures</p>
40
+ <table class='tryForm__table'>
41
+ <% if action.can_change_full? %>
42
+ <tr>
43
+ <td width="50%">Include extended attributes?</td>
44
+ <td width="50%">
56
45
  <div class='tryForm__checkbox'>
57
- <input type='checkbox' class='tryForm__expansions' name='<%= expansion%>' id="expan_<%= expansion %>" <% if action.includes_expansion?(expansion) %>checked='checked'<%end%>>
58
- <label for="expan_<%= expansion %>"><%= expansion %></label>
46
+ <input type='checkbox' class='tryForm__fullAttrs' name='full' id="full_attrs" <% if action.includes_full_attributes? %>checked='checked'<%end%>>
47
+ <label for="full_attrs">Yes - include extended attributes</label>
59
48
  </div>
60
- <% end %>
61
- </td>
62
- </tr>
63
- <% end %>
64
-
65
- </table>
49
+ </td>
50
+ </tr>
51
+ <% end %>
52
+ <% if action.can_change_expansions? %>
53
+ <tr>
54
+ <td width="50%">Include expansions?</td>
55
+ <td width="50%">
56
+ <% for expansion in action.available_expansions %>
57
+ <div class='tryForm__checkbox'>
58
+ <input type='checkbox' class='tryForm__expansions' name='<%= expansion%>' id="expan_<%= expansion %>" <% if action.includes_expansion?(expansion) %>checked='checked'<%end%>>
59
+ <label for="expan_<%= expansion %>"><%= expansion %></label>
60
+ </div>
61
+ <% end %>
62
+ </td>
63
+ </tr>
64
+ <% end %>
66
65
 
67
- <% end %>
66
+ </table>
68
67
 
69
- <p class='tryForm__button'>
70
- <button class='tryForm__buttonLink' type='submit'>Make this request</button>
71
- <button class='tryForm__buttonLink tryFormCancel' type='button'>Cancel</button>
72
- </p>
73
-
74
- <pre class='tryForm__output'>The request output will be shown here...</pre>
75
- </form>
68
+ <% end %>
76
69
 
70
+ <p class='tryForm__button'>
71
+ <button class='tryForm__buttonLink' type='submit'>Make this request</button>
72
+ <button class='tryForm__buttonLink tryFormCancel' type='button'>Cancel</button>
73
+ </p>
77
74
 
75
+ <pre class='tryForm__output'>The request output will be shown here...</pre>
76
+ </form>
77
+ <% end %>
@@ -97,11 +97,17 @@ $(document).ready(function() {
97
97
  outputBox.text(JSON.stringify(data, null, 4))
98
98
  outputBox.show()
99
99
  },
100
- error: function() {
100
+ error: function(xhr) {
101
101
  // Errors which occurr aren't very well reported at the moment.
102
102
  // They should be.
103
+ if(xhr.getResponseHeader('content-type') == 'application/json') {
104
+ var text = JSON.stringify(JSON.parse(xhr.responseText), null, 4)
105
+ } else {
106
+ var text = "Failed to make request."
107
+ }
108
+
103
109
  outputBox.show()
104
- outputBox.text("Failed to make request.")
110
+ outputBox.text(text)
105
111
  outputBox.addClass('tryForm__output--error').removeClass('tryForm__output--success')
106
112
  }
107
113
  })
@@ -6,6 +6,7 @@
6
6
  <% else %>
7
7
  <% set_page_title "#{humanize(authenticator.name.to_s.capitalize)} Authenticator" %>
8
8
  <h1><%= humanize(authenticator.name.to_s.capitalize) %> Authenticator</h1>
9
+ <% set_active_nav "authenticator-#{authenticator.name.to_s}" %>
9
10
  <% end %>
10
11
 
11
12
  <p class='text'>
@@ -17,10 +17,10 @@
17
17
  Home
18
18
  </a>
19
19
  </li>
20
- <% if base.authenticators[:default] %>
20
+ <% base.authenticators.select { |k,v| v.friendly_name }.each do |id, authenticator| %>
21
21
  <li>
22
- <a href='<%= path('authenticators/default') %>' class="<%= active_nav == 'authenticator-default' ? 'active' : '' %>">
23
- Authentication
22
+ <a href='<%= path('authenticators/' + id.to_s) %>' class="<%= active_nav == "authenticator-" + id.to_s ? 'active' : '' %>">
23
+ <%= authenticator.friendly_name %>
24
24
  </a>
25
25
  </li>
26
26
  <% end %>
@@ -68,6 +68,14 @@ class RackMiddlewareTest < Test::Unit::TestCase
68
68
  assert_equal 'Adam', response_json['data']
69
69
  end
70
70
 
71
+ def test_params_in_body_with_charset
72
+ post "/api/v1/users/echo", '{"name":"Adam"}', {'CONTENT_TYPE' => 'application/json; charset=utf8'}
73
+ assert_equal 200, last_response.status
74
+ assert response_json = JSON.parse(last_response.body)
75
+ assert_equal 'success', response_json['status']
76
+ assert_equal 'Adam', response_json['data']
77
+ end
78
+
71
79
  def test_passing_invalid_json_renders_a_bad_request
72
80
  get "/api/v1/users/list", {:params => "{invalidjson}"}
73
81
  assert_equal 400, last_response.status
@@ -332,4 +332,59 @@ class StructuresTest < Test::Unit::TestCase
332
332
  assert_equal "Bananas!", base.structure(:animal).attributes[:basic].select { |a| a.name == :example2 }.first.description
333
333
  end
334
334
 
335
+ def test_mutating_values
336
+ base = Moonrope::Base.new do
337
+ structure :animal do
338
+ basic :name, :mutation => :downcase
339
+ end
340
+ end
341
+ assert_equal 'fido', base.structure(:animal).hash(Animal.new(:name => 'FIDO'))[:name]
342
+ end
343
+
344
+ def test_auto_mutating_values
345
+ base = Moonrope::Base.new do
346
+ structure :animal do
347
+ basic :name, :type => :unix_timestamp
348
+ end
349
+ end
350
+ assert_equal 1234567890, base.structure(:animal).hash(Animal.new(:name => Time.at(1234567890)))[:name]
351
+ end
352
+
353
+ def test_manually_selecting_attributes
354
+ base = Moonrope::Base.new do
355
+ structure :animal do
356
+ basic :id
357
+ basic :name
358
+ end
359
+ end
360
+ hash = base.structure(:animal).hash(Animal.new(:id => 12345, :name => "Fido"), :attributes => [:id])
361
+ assert_equal 12345, hash[:id]
362
+ assert_equal false, hash.has_key?(:name)
363
+ end
364
+
365
+
366
+ def test_setting_options_for_embedded_expansions
367
+ user = User.new(:id => 1, :username => 'dave')
368
+ animal1 = Animal.new(:id => 1, :name => 'Fido', :color => 'Ginger', :user => user)
369
+ animal2 = Animal.new(:id => 2, :name => 'Jess', :color => 'Black & White', :user => user)
370
+ user.animals << animal1
371
+ user.animals << animal2
372
+
373
+ base = Moonrope::Base.new do
374
+ structure :user do
375
+ basic { {:id => o.id, :username => o.username } }
376
+ expansion :animals, :structure => :animal
377
+ end
378
+
379
+ structure :animal do
380
+ basic :id
381
+ basic :name
382
+ full :color
383
+ end
384
+ end
385
+
386
+ hash = base.structure(:user).hash(user, :expansions => [{:animals => {:full => true}}])
387
+ assert_equal 'Ginger', hash[:animals][0][:color]
388
+ end
389
+
335
390
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moonrope
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Cooke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2017-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -192,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
192
  version: '0'
193
193
  requirements: []
194
194
  rubyforge_project:
195
- rubygems_version: 2.4.5
195
+ rubygems_version: 2.5.1
196
196
  signing_key:
197
197
  specification_version: 4
198
198
  summary: An API server DSL.