intelligence 0.5.0 → 0.7.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +555 -0
  3. data/intelligence.gemspec +1 -1
  4. data/lib/intelligence/adapter/base.rb +23 -3
  5. data/lib/intelligence/adapter/class_methods.rb +15 -0
  6. data/lib/intelligence/adapter/{construction_methods.rb → module_methods.rb} +8 -4
  7. data/lib/intelligence/adapter.rb +2 -2
  8. data/lib/intelligence/adapters/anthropic/adapter.rb +21 -30
  9. data/lib/intelligence/adapters/anthropic/chat_request_methods.rb +189 -0
  10. data/lib/intelligence/adapters/anthropic/{chat_methods.rb → chat_response_methods.rb} +8 -124
  11. data/lib/intelligence/adapters/cerebras.rb +17 -17
  12. data/lib/intelligence/adapters/generic/adapter.rb +1 -12
  13. data/lib/intelligence/adapters/generic/chat_methods.rb +42 -11
  14. data/lib/intelligence/adapters/generic.rb +1 -1
  15. data/lib/intelligence/adapters/google/adapter.rb +33 -35
  16. data/lib/intelligence/adapters/google/chat_request_methods.rb +233 -0
  17. data/lib/intelligence/adapters/google/{chat_methods.rb → chat_response_methods.rb} +52 -162
  18. data/lib/intelligence/adapters/groq.rb +46 -28
  19. data/lib/intelligence/adapters/hyperbolic.rb +13 -13
  20. data/lib/intelligence/adapters/legacy/adapter.rb +0 -2
  21. data/lib/intelligence/adapters/legacy/chat_methods.rb +22 -6
  22. data/lib/intelligence/adapters/mistral.rb +57 -0
  23. data/lib/intelligence/adapters/open_ai/adapter.rb +38 -45
  24. data/lib/intelligence/adapters/open_ai/chat_request_methods.rb +186 -0
  25. data/lib/intelligence/adapters/open_ai/{chat_methods.rb → chat_response_methods.rb} +60 -131
  26. data/lib/intelligence/adapters/open_ai.rb +1 -1
  27. data/lib/intelligence/adapters/open_router.rb +62 -0
  28. data/lib/intelligence/adapters/samba_nova.rb +13 -13
  29. data/lib/intelligence/adapters/together_ai.rb +21 -19
  30. data/lib/intelligence/chat_request.rb +57 -7
  31. data/lib/intelligence/chat_result.rb +4 -0
  32. data/lib/intelligence/chat_result_choice.rb +4 -2
  33. data/lib/intelligence/conversation.rb +38 -9
  34. data/lib/intelligence/message.rb +103 -20
  35. data/lib/intelligence/message_content/base.rb +3 -0
  36. data/lib/intelligence/message_content/binary.rb +6 -0
  37. data/lib/intelligence/message_content/file.rb +35 -0
  38. data/lib/intelligence/message_content/text.rb +5 -0
  39. data/lib/intelligence/message_content/tool_call.rb +12 -1
  40. data/lib/intelligence/message_content/tool_result.rb +15 -3
  41. data/lib/intelligence/message_content.rb +12 -3
  42. data/lib/intelligence/tool.rb +139 -0
  43. data/lib/intelligence/version.rb +1 -1
  44. data/lib/intelligence.rb +6 -4
  45. metadata +18 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 694776aeafc7552879d8b9d5358907e62c90a6fc5a1219081ee1d1a4cb5ddeef
4
- data.tar.gz: 380f5cd13200750f65938fd4e92c21218af8c2b57af0614bfe16f714ba296e68
3
+ metadata.gz: 910d4c8472c375a7a3759c474d62e71c01395136d895ccff9dd2f33012fd5a39
4
+ data.tar.gz: 86b51ac28f93a39556664c3707f341d40fe5aa4ec1cdc27a217586fd222c0c77
5
5
  SHA512:
6
- metadata.gz: f2ca65b2a4e3a202bc1b331f7965da9e76a8ccd2ec83f7c5c273c94137fb1e0131226ecf3bf2aed6d50e262933d26c0024b3a5052edda28c39768c766b508e29
7
- data.tar.gz: c6694ffa62ddd9eb766f42fb9cc073195a01e11e0d34cd346588b3923266c14f794d1711e1766888369874e27ce4ac696de0513055af8e27149a778611ce0cb2
6
+ metadata.gz: 6d47d1f1d333cb1f0ffe8bdb06bf27ad9be4c675349c01460f97021cc733df2410ed7bc597324b3413d44d0ed081a7a0ae129f0173313f4df5400d9012ed37b9
7
+ data.tar.gz: 90f91d3efdf84c091252d4e16dbbcab41a7f27b39350974a62d80eee37d465f1dfc000b1efb836471d00d47d9a774050cf0f28c3ada96d6ba9947d8f54a6c209
data/README.md ADDED
@@ -0,0 +1,555 @@
1
+ # Intelligence
2
+
3
+ Intelligence is a lightweight yet powerful Ruby gem that provides a uniform interface for
4
+ interacting with large language and vision model APIs across multiple providers. It allows
5
+ you to seamlessly integrate with services from OpenAI, Anthropic, Google, Cerebras, Groq,
6
+ Hyperbolic, Samba Nova, Together AI, and others, while maintaining a consistent API across
7
+ all providers.
8
+
9
+ The gem operates with minimal dependencies and doesn't require vendor SDK installation,
10
+ making it easy to switch between providers or work with multiple providers simultaneously.
11
+
12
+ ```
13
+ require 'intelligence'
14
+
15
+ adapter = Intelligence::Adapter.build :open_ai do
16
+ key ENV[ 'OPENAI_API_KEY' ]
17
+ chat_options do
18
+ model 'gpt-4o'
19
+ max_tokens 256
20
+ end
21
+ end
22
+
23
+ request = Intelligence::ChatRequest.new( adapter: adapter )
24
+ conversation = Intelligence::Conversation.build do
25
+ system_message do
26
+ content text: "You are a highly efficient AI assistant. Provide clear, concise responses."
27
+ end
28
+ message role: :user do
29
+ content text: ARGV[ 0 ] || 'Hello!'
30
+ end
31
+ end
32
+
33
+ response = request.chat( conversation )
34
+
35
+ if response.success?
36
+ puts response.result.text
37
+ else
38
+ puts "Error: " + response.result.error_description
39
+ end
40
+ ```
41
+
42
+ ## Installation
43
+
44
+ Add this line to your application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'intelligence'
48
+ ```
49
+
50
+ Then execute:
51
+
52
+ ```bash
53
+ $ bundle install
54
+ ```
55
+
56
+ Or install it directly:
57
+
58
+ ```bash
59
+ $ gem install intelligence
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ### Minimal Chat Request
65
+
66
+ The core components of Intelligence are adapters, requests and responses. An adapter encapsulates
67
+ the differences between different providers allowing you to use requests and responses uniformly.
68
+
69
+ You retrieve an adapter for a specific vendor, configure it with a key, model and associated
70
+ parameters and then make a request by calling either the `chat` or `stream` methods.
71
+
72
+ ```ruby
73
+ require 'intelligence'
74
+
75
+ # configure the adapter with your API key and model settings
76
+ adapter = Intelligence::Adapter[ :google ].new(
77
+ key: ENV[ 'GOOGLE_API_KEY' ],
78
+ chat_options: {
79
+ model: 'gemini-1.5-flash-002',
80
+ max_tokens: 256
81
+ }
82
+ )
83
+
84
+ # create a request instance, passing the adapter
85
+ request = Intelligence::ChatRequest.new( adapter: adapter )
86
+
87
+ # make the request and handle the response
88
+ response = request.chat( "What is the capital of France?" )
89
+
90
+ if response.success?
91
+ puts response.result.text
92
+ else
93
+ puts "Error: #{response.result.error_description}"
94
+ end
95
+ ```
96
+
97
+ The `response` object is a Faraday response with an added method: `result`. If a response is
98
+ successful `result` returns a `ChatResult`. If it is not successful it returns a
99
+ `ChatErrorResult`.
100
+
101
+ ### Understanding Results
102
+
103
+ When you make a request using Intelligence, the response includes a `result` that provides
104
+ structured access to the model's output.
105
+
106
+ - A `ChatResult` contains one or more `choices` (alternate responses from the model). The
107
+ `choices` method returns an array of `ChatResultChoice` instances. It also includes
108
+ a `metrics` methods which provides information about token usage for the request.
109
+ optional `metrics` about token usage
110
+ - A `ChatResultChoice` contains a `message` from the assistant and an `end_result` which
111
+ indicates how the response ended;
112
+ - `:ended` means the model completed its response normally
113
+ - `:token_limit_exceeded` means the response hit the token limit ( `max_tokens` )
114
+ - `:end_sequence_encountered` means the response hit a stop sequence
115
+ - `:filtered` means the content was filtered by safety settings
116
+ - `:tool_called` means the model is requesting to use a tool
117
+ - The `Message` in each choice contains one or more content items, typically text but
118
+ potentially tool calls or other content types.
119
+
120
+ While the convenience method `text` used in the previous example is useful for simple cases,
121
+ you will typically want to work with the full response structure.
122
+
123
+ ```ruby
124
+ adapter = Intelligence::Adapter[ :google ].new(
125
+ key: ENV[ 'GOOGLE_API_KEY' ],
126
+ chat_options: {
127
+ model: 'gemini-1.5-flash-002',
128
+ max_tokens: 256
129
+ }
130
+ )
131
+
132
+ request = Intelligence::ChatRequest.new( adapter: adapter )
133
+ response = request.chat( "What are three interesting facts about ruby gemstones?" )
134
+
135
+ if response.success?
136
+ result = response.result # this is a ChatResult
137
+
138
+ # iterate through the model's choices
139
+ result.choices.each do | choice |
140
+ # check why the response ended
141
+ puts "Response ended because: #{choice.end_reason}"
142
+
143
+ # work with the message
144
+ message = choice.message
145
+ puts "Message role: #{message.role}"
146
+
147
+ # examine each piece of content
148
+ message.each_content do | content |
149
+ puts content.text if content.is_a?( Intelligence::MessageContent::Text )
150
+ end
151
+ end
152
+
153
+ # check token usage if metrics are available
154
+ if result.metrics
155
+ puts "Input tokens: #{result.metrics.input_tokens}"
156
+ puts "Output tokens: #{result.metrics.output_tokens}"
157
+ puts "Total tokens: #{result.metrics.total_tokens}"
158
+ end
159
+ else
160
+ # or alternativelly handle the end result
161
+ puts "Error: #{response.result.error_description}"
162
+ end
163
+ ```
164
+
165
+ The `ChatResult`, `ChatResultChoice` and `Message` all provide the `text` convenience
166
+ method which return the text.
167
+
168
+ A response might end for various reasons, indicated by the `end_reason` in each choice:
169
+ - `:ended` means the model completed its response normally
170
+ - `:token_limit_exceeded` means the response hit the token limit
171
+ - `:end_sequence_encountered` means the response hit a stop sequence
172
+ - `:filtered` means the content was filtered by safety settings
173
+ - `:tool_called` means the model is requesting to use a tool
174
+
175
+ ### Understanding Conversations, Messages, and Content
176
+
177
+ Intelligence organizes interactions with models using three main components:
178
+
179
+ - **Conversations** are collections of messages that represent a complete interaction with a
180
+ model. A conversation can include an optional system message that sets the context, and a
181
+ series of back-and-forth messages between the user and assistant.
182
+
183
+ - **Messages** are individual communications within a conversation. Each message has a role
184
+ (`:system`, `:user`, or `:assistant`) that identifies its sender and can contain multiple
185
+ pieces of content.
186
+
187
+ - **Content** represents the actual data within a message. This can be text
188
+ (`MessageContent::Text`), binary data like images (`MessageContent::Binary`), or references
189
+ to files (`MessageContent::File`).
190
+
191
+ In the previous examples we used a simple string as an argument to `chat`. As a convenience,
192
+ the `chat` methods builds a coversation for you but, typically, you will construct a coversation
193
+ instance (`Coversation`) and pass that to the chat or stream methods.
194
+
195
+ The following example expands the minimal example, building a conversation, messages and content:
196
+
197
+ ```ruby
198
+ # create an adapter as before
199
+ adapter = Intelligence::Adapter[ :google ].new(
200
+ key: ENV[ 'GOOGLE_API_KEY' ],
201
+ chat_options: { model: 'gemini-1.5-flash-002', max_tokens: 256 }
202
+ )
203
+
204
+ # create a conversation
205
+ conversation = Intelligence::Conversation.new
206
+
207
+ # add a system message (optional but recommended)
208
+ system_message = Intelligence::Message.new( :system )
209
+ system_message << Intelligence::MessageContent::Text.new(
210
+ text: "You are a helpful coding assistant."
211
+ )
212
+ conversation.system_message = system_message
213
+
214
+ # add a user message
215
+ user_message = Intelligence::Message.new( :user )
216
+ user_message << Intelligence::MessageContent::Text.new(
217
+ text: "How do I read a file in Ruby?"
218
+ )
219
+ conversation.messages << user_message
220
+
221
+ # make the request
222
+ request = Intelligence::ChatRequest.new( adapter: adapter )
223
+ response = request.chat( conversation )
224
+
225
+ if response.success?
226
+ puts response.result.text
227
+ else
228
+ puts "Error: #{response.result.error_description}"
229
+ end
230
+ ```
231
+
232
+ The hierarchical nature of these components makes it easy to organize and access your interaction
233
+ data. A conversation acts as a container for messages, and each message acts as a container for
234
+ content items. This structure allows for rich interactions that can include multiple types of
235
+ content in a single message.
236
+
237
+ You can examine the contents of a conversation by iterating through its messages and their content:
238
+
239
+ ```ruby
240
+ # iterate through messages
241
+ conversation.messages.each do |message|
242
+ puts "Role: #{message.role}"
243
+
244
+ # each message can have multiple content items
245
+ message.each_content do |content|
246
+ case content
247
+ when Intelligence::MessageContent::Text
248
+ puts "Text: #{content.text}"
249
+ when Intelligence::MessageContent::Binary
250
+ puts "Binary content of type: #{content.content_type}"
251
+ when Intelligence::MessageContent::File
252
+ puts "File reference: #{content.uri}"
253
+ end
254
+ end
255
+ end
256
+
257
+ # remeber that, alternatively, you can use convenience methods for quick text access
258
+ puts message.text # combines all text content in a messages with newlines
259
+ ```
260
+ ### Continuing Conversations / Maintaining Context
261
+
262
+ To continue a conversation with the model, we can add the model's response and our follow-up
263
+ message to the conversation:
264
+
265
+ ```ruby
266
+ # get the previous response
267
+ if response.success?
268
+ # add the assistant's response to our conversation
269
+ assistant_message = response.result.message
270
+ conversation.messages << assistant_message
271
+
272
+ # add another user message for follow-up
273
+ follow_up = Intelligence::Message.new( :user )
274
+ follow_up << Intelligence::MessageContent::Text.new(
275
+ text: "How do I write to that file?"
276
+ )
277
+ conversation.messages << follow_up
278
+
279
+ # make another request with the updated conversation
280
+ response = request.chat( conversation )
281
+
282
+ if response.success?
283
+ puts response.result.text
284
+ end
285
+ end
286
+ ```
287
+
288
+ This pattern allows you to maintain context across multiple interactions with the model. Each
289
+ request includes the full conversation history, helping the model provide more contextually
290
+ relevant responses.
291
+
292
+ ### Using Builders
293
+
294
+ For more readable configuration, Intelligence provides builder syntax for both adapters and
295
+ conversations.
296
+
297
+ ```ruby
298
+ adapter = Intelligence::Adapter.build! :google do
299
+ key ENV['GOOGLE_API_KEY']
300
+ chat_options do
301
+ model 'gemini-1.5-flash-002'
302
+ max_tokens 256
303
+ temperature 0.7
304
+ end
305
+ end
306
+ ```
307
+
308
+ Similarly, you can use builders to construct conversations with multiple messages.
309
+
310
+ ```ruby
311
+ conversation = Intelligence::Conversation.build do
312
+ system_message do
313
+ content text: "You are a knowledgeable historian specializing in ancient civilizations."
314
+ end
315
+
316
+ message do
317
+ role :user
318
+ content text: "What were the key factors in the fall of the Roman Empire?"
319
+ end
320
+ end
321
+
322
+ request = Intelligence::ChatRequest.new( adapter: adapte r)
323
+ response = request.chat( conversation )
324
+ ```
325
+
326
+ ## Binary and File Content
327
+
328
+ Intelligence supports vision models through binary and file content types.
329
+
330
+ ```ruby
331
+ require 'intelligence'
332
+ require 'mime-types'
333
+
334
+ adapter = Intelligence::Adapter.build! :open_ai do
335
+ key ENV[ 'OPENAI_API_KEY' ]
336
+ chat_options do
337
+ model 'gpt-4-vision-preview'
338
+ max_tokens 256
339
+ end
340
+ end
341
+
342
+ # Using binary content for local images
343
+ conversation = Intelligence::Conversation.build do
344
+ message do
345
+ role :user
346
+ content text: "What's in this image?"
347
+ content do
348
+ type :binary
349
+ content_type 'image/jpeg'
350
+ bytes File.binread( 'path/to/image.jpg' )
351
+ end
352
+ end
353
+ end
354
+
355
+ request = Intelligence::ChatRequest.new( adapter: adapter )
356
+ response = request.chat( conversation )
357
+ ```
358
+
359
+ For remote images, you can use file content instead of binary content:
360
+
361
+ ```ruby
362
+ conversation = Intelligence::Conversation.build do
363
+ message do
364
+ role :user
365
+ content text: "Analyze this image"
366
+ content do
367
+ type :file
368
+ content_type 'image/jpeg'
369
+ uri 'https://example.com/image.jpg'
370
+ end
371
+ end
372
+ end
373
+ ```
374
+
375
+ ## Tools
376
+
377
+ Intelligence supports tool/function calling capabilities, allowing models to
378
+ use defined tools during their response.
379
+
380
+ ```ruby
381
+ adapter = Intelligence::Adapter.build! :anthropic do
382
+ key ENV['ANTHROPIC_API_KEY']
383
+ chat_options do
384
+ model 'claude-3-5-sonnet-20240620'
385
+ max_tokens 1024
386
+ end
387
+ end
388
+
389
+ # Define a tool for getting weather information
390
+ weather_tool = Intelligence::Tool.build! do
391
+ name :get_weather
392
+ description "Get the current weather for a specified location"
393
+ argument name: :location, required: true, type: 'object' do
394
+ description "The location for which to retrieve weather information"
395
+ property name: :city, type: 'string', required: true do
396
+ description "The city or town name"
397
+ end
398
+ property name: :state, type: 'string' do
399
+ description "The state or province (optional)"
400
+ end
401
+ property name: :country, type: 'string' do
402
+ description "The country (optional)"
403
+ end
404
+ end
405
+ end
406
+
407
+ # Create a conversation with the tool
408
+ conversation = Intelligence::Conversation.build do
409
+ system_message do
410
+ content text: "You can help users check weather conditions."
411
+ end
412
+
413
+ # Add the tool to the conversation
414
+ tools << weather_tool
415
+
416
+ message do
417
+ role :user
418
+ content text: "What's the weather like in Paris, France?"
419
+ end
420
+ end
421
+
422
+ request = Intelligence::ChatRequest.new( adapter: adapter )
423
+ response = request.chat( conversation )
424
+
425
+ # Handle tool calls in the response
426
+ if response.success?
427
+ result.choices.each do |choice|
428
+ choice.message.each_content do |content|
429
+ if content.is_a?(Intelligence::MessageContent::ToolCall)
430
+ # Process the tool call
431
+ if content.tool_name == :get_weather
432
+ # Make actual weather API call here
433
+ weather_data = fetch_weather(content.tool_parameters[:location])
434
+
435
+ # Send tool result back to continue the conversation
436
+ conversation.messages << Intelligence::Message.build! do
437
+ role :user
438
+ content do
439
+ type :tool_result
440
+ tool_call_id content.tool_call_id
441
+ tool_result weather_data.to_json
442
+ end
443
+ end
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
449
+ ```
450
+
451
+ Tools are defined using the `Intelligence::Tool.build!` method, where you specify the tool's
452
+ name, description, and its argument schema. Arguments can have nested properties with their
453
+ own descriptions and requirements. Once defined, tools are added to conversations and can be
454
+ used by the model during its response.
455
+
456
+ Note that not all providers support tools, and the specific tool capabilities may vary between
457
+ providers. Check your provider's documentation for details on tool support and requirements.
458
+
459
+ ## Streaming Responses
460
+
461
+ Once you're familiar with basic requests, you might want to use streaming for real-time
462
+ responses. Streaming delivers the model's response in chunks as it's generated:
463
+
464
+ ```ruby
465
+ adapter = Intelligence::Adapter.build! :anthropic do
466
+ key ENV['ANTHROPIC_API_KEY']
467
+ chat_options do
468
+ model 'claude-3-5-sonnet-20240620'
469
+ max_tokens 1024
470
+ stream true
471
+ end
472
+ end
473
+
474
+ request = Intelligence::ChatRequest.new(adapter: adapter)
475
+
476
+ response = request.stream("Tell me a story about a robot.") do |request|
477
+ request.receive_result do |result|
478
+ # result is a ChatResult object with partial content
479
+ print result.text
480
+ print "\n" if result.choices.first.end_reason
481
+ end
482
+ end
483
+ ```
484
+
485
+ Streaming also works with complex conversations and binary content:
486
+
487
+ ```ruby
488
+ conversation = Intelligence::Conversation.build do
489
+ system_message do
490
+ content text: "You are an image analysis expert."
491
+ end
492
+
493
+ message do
494
+ role :user
495
+ content text: "Describe this image in detail"
496
+ content do
497
+ type :binary
498
+ content_type 'image/jpeg'
499
+ bytes File.binread('path/to/image.jpg')
500
+ end
501
+ end
502
+ end
503
+
504
+ response = request.stream(conversation) do |request|
505
+ request.receive_result do |result|
506
+ result.choices.each do |choice|
507
+ choice.message.each_content do |content|
508
+ print content.text if content.is_a?(Intelligence::MessageContent::Text)
509
+ end
510
+ end
511
+ end
512
+ end
513
+ ```
514
+
515
+ ## Provider Switching
516
+
517
+ One of Intelligence's most powerful features is the ability to easily switch between providers:
518
+
519
+ ```ruby
520
+ def create_adapter(provider, api_key, model)
521
+ Intelligence::Adapter.build! provider do
522
+ key api_key
523
+ chat_options do
524
+ model model
525
+ max_tokens 256
526
+ end
527
+ end
528
+ end
529
+
530
+ # Create adapters for different providers
531
+ anthropic = create_adapter(:anthropic, ENV['ANTHROPIC_API_KEY'], 'claude-3-5-sonnet-20240620')
532
+ google = create_adapter(:google, ENV['GOOGLE_API_KEY'], 'gemini-1.5-pro-002')
533
+ openai = create_adapter(:open_ai, ENV['OPENAI_API_KEY'], 'gpt-4o')
534
+
535
+ # Use the same conversation with different providers
536
+ conversation = Intelligence::Conversation.build do
537
+ system_message do
538
+ content text: "You are a helpful assistant."
539
+ end
540
+ message do
541
+ role :user
542
+ content text: "Explain quantum entanglement in simple terms."
543
+ end
544
+ end
545
+
546
+ [anthropic, google, open_ai].each do |adapter|
547
+ request = Intelligence::ChatRequest.new(adapter: adapter)
548
+ response = request.chat(conversation)
549
+ puts "#{adapter.class.name} response: #{response.result.text}"
550
+ end
551
+ ```
552
+
553
+ ## License
554
+
555
+ This gem is available as open source under the terms of the MIT License.
data/intelligence.gemspec CHANGED
@@ -37,7 +37,7 @@ Gem::Specification.new do | spec |
37
37
  spec.require_paths = [ "lib" ]
38
38
 
39
39
  spec.add_runtime_dependency 'faraday', '~> 2.7'
40
- spec.add_runtime_dependency 'adaptiveconfiguration', '~> 1.0.0.beta01'
40
+ spec.add_runtime_dependency 'dynamicschema', '~> 1.0.0.beta03'
41
41
  spec.add_runtime_dependency 'mime-types', '~> 3.6'
42
42
 
43
43
  spec.add_development_dependency 'rspec', '~> 3.4'
@@ -1,7 +1,27 @@
1
+ require_relative 'class_methods'
2
+
1
3
  module Intelligence
2
4
  module Adapter
3
5
  class Base
4
- extend AdaptiveConfiguration::Configurable
5
- end
6
+ include DynamicSchema::Definable
7
+ extend ClassMethods
8
+
9
+ def initialize( options = {}, configuration: nil )
10
+ @options = build_options( options )
11
+ @options = configuration.merge( @options ) if configuration&.any?
12
+ end
13
+
14
+ protected
15
+ attr_reader :options
16
+
17
+ private
18
+
19
+ def build_options( options )
20
+ return {} unless options&.any?
21
+ self.class.builder.build( options )
22
+ end
23
+
24
+ end
6
25
  end
7
- end
26
+ end
27
+
@@ -0,0 +1,15 @@
1
+ module Intelligence
2
+ module Adapter
3
+ module ClassMethods
4
+
5
+ def build( options = nil, &block )
6
+ new( configuration: builder.build( options, &block ) )
7
+ end
8
+
9
+ def build!( options = nil, &block )
10
+ new( configuration: builder.build( options, &block ) )
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  module Intelligence
2
2
  module Adapter
3
- module ConstructionMethods
3
+ module ModuleMethods
4
4
 
5
5
  def []( adapter_type )
6
6
 
@@ -21,15 +21,19 @@ module Intelligence
21
21
  adapter_class = Intelligence.const_get( class_name ) rescue nil
22
22
  end
23
23
 
24
- raise ArgumentError.new( "An unknown Intelligence adapter #{adapter_type} was configured." ) \
24
+ raise ArgumentError.new( "An unknown Intelligence adapter #{adapter_type} was requested." ) \
25
25
  if adapter_class.nil?
26
26
 
27
27
  adapter_class
28
28
 
29
29
  end
30
30
 
31
- def build( adapter_type, attributes, &block )
32
- self.[]( adapter_type ).new( attributes, &block )
31
+ def build( adapter_type, attributes = nil, &block )
32
+ self.[]( adapter_type ).build( attributes, &block )
33
+ end
34
+
35
+ def build!( adapter_type, attributes = nil, &block )
36
+ self.[]( adapter_type ).build!( attributes, &block )
33
37
  end
34
38
 
35
39
  end
@@ -1,8 +1,8 @@
1
- require_relative 'adapter/construction_methods'
1
+ require_relative 'adapter/module_methods'
2
2
  require_relative 'adapter/base'
3
3
 
4
4
  module Intelligence
5
5
  module Adapter
6
- extend ConstructionMethods
6
+ extend ModuleMethods
7
7
  end
8
8
  end