lluminary 0.1.1 → 0.1.2

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lluminary/config.rb +11 -1
  3. data/lib/lluminary/field_description.rb +34 -29
  4. data/lib/lluminary/provider_error.rb +2 -1
  5. data/lib/lluminary/providers/base.rb +5 -1
  6. data/lib/lluminary/providers/bedrock.rb +32 -30
  7. data/lib/lluminary/providers/openai.rb +28 -21
  8. data/lib/lluminary/providers/test.rb +9 -14
  9. data/lib/lluminary/result.rb +5 -2
  10. data/lib/lluminary/schema.rb +9 -16
  11. data/lib/lluminary/schema_model.rb +13 -10
  12. data/lib/lluminary/task.rb +84 -59
  13. data/lib/lluminary/validation_error.rb +2 -1
  14. data/lib/lluminary/version.rb +3 -2
  15. data/lib/lluminary.rb +23 -7
  16. data/spec/examples/analyze_text_spec.rb +7 -4
  17. data/spec/examples/color_analyzer_spec.rb +22 -22
  18. data/spec/examples/content_analyzer_spec.rb +27 -44
  19. data/spec/examples/historical_event_analyzer_spec.rb +18 -15
  20. data/spec/examples/price_analyzer_spec.rb +22 -28
  21. data/spec/examples/quote_task_spec.rb +9 -8
  22. data/spec/examples/sentiment_analysis_spec.rb +13 -10
  23. data/spec/examples/summarize_text_spec.rb +7 -4
  24. data/spec/lluminary/config_spec.rb +28 -26
  25. data/spec/lluminary/field_description_spec.rb +19 -21
  26. data/spec/lluminary/providers/base_spec.rb +11 -8
  27. data/spec/lluminary/providers/bedrock_spec.rb +47 -57
  28. data/spec/lluminary/providers/openai_spec.rb +27 -35
  29. data/spec/lluminary/providers/test_spec.rb +21 -16
  30. data/spec/lluminary/result_spec.rb +17 -10
  31. data/spec/lluminary/schema_model_spec.rb +31 -22
  32. data/spec/lluminary/schema_spec.rb +95 -107
  33. data/spec/lluminary/task_spec.rb +366 -300
  34. data/spec/spec_helper.rb +7 -2
  35. metadata +35 -19
@@ -1,8 +1,11 @@
1
- require 'spec_helper'
2
- require_relative '../../examples/summarize_text'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+ require_relative "../../examples/summarize_text"
3
4
 
4
5
  RSpec.describe SummarizeText do
5
- let(:text) { "Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write." }
6
+ let(:text) { <<~TEXT }
7
+ Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
8
+ TEXT
6
9
 
7
10
  it "summarizes text" do
8
11
  result = described_class.call(text: text)
@@ -18,4 +21,4 @@ RSpec.describe SummarizeText do
18
21
  expect(json).to have_key("summary")
19
22
  expect(json["summary"]).to eq(result.output.summary)
20
23
  end
21
- end
24
+ end
@@ -1,47 +1,49 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  RSpec.describe Lluminary::Config do
4
5
  let(:config) { described_class.new }
5
6
 
6
- describe '#configure' do
7
- it 'allows setting provider configurations' do
7
+ describe "#configure" do
8
+ it "allows setting provider configurations" do
8
9
  config.configure do |c|
9
- c.provider(:openai, api_key: 'global-key')
10
- c.provider(:bedrock,
11
- access_key_id: 'global-access-key',
12
- secret_access_key: 'global-secret-key',
13
- region: 'us-east-1'
10
+ c.provider(:openai, api_key: "global-key")
11
+ c.provider(
12
+ :bedrock,
13
+ access_key_id: "global-access-key",
14
+ secret_access_key: "global-secret-key",
15
+ region: "us-east-1"
14
16
  )
15
17
  end
16
18
 
17
- expect(config.provider_config(:openai)).to eq({ api_key: 'global-key' })
18
- expect(config.provider_config(:bedrock)).to eq({
19
- access_key_id: 'global-access-key',
20
- secret_access_key: 'global-secret-key',
21
- region: 'us-east-1'
22
- })
19
+ expect(config.provider_config(:openai)).to eq({ api_key: "global-key" })
20
+ expect(config.provider_config(:bedrock)).to eq(
21
+ {
22
+ access_key_id: "global-access-key",
23
+ secret_access_key: "global-secret-key",
24
+ region: "us-east-1"
25
+ }
26
+ )
23
27
  end
24
28
  end
25
29
 
26
- describe '#provider_config' do
27
- it 'returns empty hash for unconfigured providers' do
30
+ describe "#provider_config" do
31
+ it "returns empty hash for unconfigured providers" do
28
32
  expect(config.provider_config(:unknown)).to eq({})
29
33
  end
30
34
 
31
- it 'returns the configuration for a configured provider' do
32
- config.configure do |c|
33
- c.provider(:openai, api_key: 'test-key')
34
- end
35
+ it "returns the configuration for a configured provider" do
36
+ config.configure { |c| c.provider(:openai, api_key: "test-key") }
35
37
 
36
- expect(config.provider_config(:openai)).to eq({ api_key: 'test-key' })
38
+ expect(config.provider_config(:openai)).to eq({ api_key: "test-key" })
37
39
  end
38
40
  end
39
41
 
40
- describe '#reset!' do
41
- it 'clears all provider configurations' do
42
+ describe "#reset!" do
43
+ it "clears all provider configurations" do
42
44
  config.configure do |c|
43
- c.provider(:openai, api_key: 'test-key')
44
- c.provider(:bedrock, access_key_id: 'test-access-key')
45
+ c.provider(:openai, api_key: "test-key")
46
+ c.provider(:bedrock, access_key_id: "test-access-key")
45
47
  end
46
48
 
47
49
  config.reset!
@@ -50,4 +52,4 @@ RSpec.describe Lluminary::Config do
50
52
  expect(config.provider_config(:bedrock)).to eq({})
51
53
  end
52
54
  end
53
- end
55
+ end
@@ -1,36 +1,34 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  RSpec.describe Lluminary::FieldDescription do
4
- describe '#to_s' do
5
- it 'generates a description for a string field' do
6
- field = {
7
- type: :string,
8
- description: 'A test field'
9
- }
10
- description = described_class.new('test_field', field)
11
- expect(description.to_s).to eq('test_field (string): A test field')
5
+ describe "#to_s" do
6
+ it "generates a description for a string field" do
7
+ field = { type: :string, description: "A test field" }
8
+ description = described_class.new("test_field", field)
9
+ expect(description.to_s).to eq("test_field (string): A test field")
12
10
  end
13
11
 
14
- it 'generates a description for a field without a description' do
15
- field = {
16
- type: :integer
17
- }
18
- description = described_class.new('count', field)
19
- expect(description.to_s).to eq('count (integer)')
12
+ it "generates a description for a field without a description" do
13
+ field = { type: :integer }
14
+ description = described_class.new("count", field)
15
+ expect(description.to_s).to eq("count (integer)")
20
16
  end
21
17
 
22
- it 'includes validation descriptions when present' do
18
+ it "includes validation descriptions when present" do
23
19
  field = {
24
20
  type: :string,
25
- description: 'A test field',
21
+ description: "A test field",
26
22
  validations: [
27
23
  [{}, { length: { minimum: 5, maximum: 10 } }],
28
- [{}, { format: { with: '/^[A-Z]+$/' } }]
24
+ [{}, { format: { with: "/^[A-Z]+$/" } }]
29
25
  ]
30
26
  }
31
- description = described_class.new('test_field', field)
32
- expected = 'test_field (string): A test field (must be at least 5 characters, must be at most 10 characters, must match format: /^[A-Z]+$/)'
27
+ description = described_class.new("test_field", field)
28
+ expected = <<~DESCRIPTION.chomp
29
+ test_field (string): A test field (must be at least 5 characters, must be at most 10 characters, must match format: /^[A-Z]+$/)
30
+ DESCRIPTION
33
31
  expect(description.to_s).to eq(expected)
34
32
  end
35
33
  end
36
- end
34
+ end
@@ -1,17 +1,20 @@
1
- require 'lluminary'
1
+ # frozen_string_literal: true
2
+ require "lluminary"
2
3
 
3
4
  RSpec.describe Lluminary::Providers::Base do
4
- describe '#initialize' do
5
- it 'accepts configuration options' do
6
- config = { api_key: 'test_key', model: 'test_model' }
5
+ describe "#initialize" do
6
+ it "accepts configuration options" do
7
+ config = { api_key: "test_key", model: "test_model" }
7
8
  provider = described_class.new(**config)
8
9
  expect(provider.config).to eq(config)
9
10
  end
10
11
  end
11
12
 
12
- describe '#call' do
13
- it 'raises NotImplementedError' do
14
- expect { described_class.new.call('test', double('Task')) }.to raise_error(NotImplementedError)
13
+ describe "#call" do
14
+ it "raises NotImplementedError" do
15
+ expect do
16
+ described_class.new.call("test", double("Task"))
17
+ end.to raise_error(NotImplementedError)
15
18
  end
16
19
  end
17
- end
20
+ end
@@ -1,109 +1,99 @@
1
- require 'spec_helper'
2
- require 'lluminary/providers/bedrock'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+ require "lluminary/providers/bedrock"
3
4
 
4
5
  RSpec.describe Lluminary::Providers::Bedrock do
5
6
  let(:config) do
6
7
  {
7
- region: 'us-east-1',
8
- access_key_id: 'test-key',
9
- secret_access_key: 'test-secret'
8
+ region: "us-east-1",
9
+ access_key_id: "test-key",
10
+ secret_access_key: "test-secret"
10
11
  }
11
12
  end
12
13
  let(:provider) { described_class.new(**config) }
13
14
 
14
- describe '#client' do
15
- it 'returns the AWS Bedrock client instance' do
15
+ describe "#client" do
16
+ it "returns the AWS Bedrock client instance" do
16
17
  expect(provider.client).to be_a(Aws::BedrockRuntime::Client)
17
18
  end
18
19
  end
19
20
 
20
- describe '#call' do
21
- let(:prompt) { 'Test prompt' }
22
- let(:task) { 'Test task' }
21
+ describe "#call" do
22
+ let(:prompt) { "Test prompt" }
23
+ let(:task) { "Test task" }
23
24
  let(:mock_response) do
24
25
  OpenStruct.new(
25
- output: OpenStruct.new(
26
- message: OpenStruct.new(
27
- content: [
28
- OpenStruct.new(text: '{"sentiment": "positive"}')
29
- ]
26
+ output:
27
+ OpenStruct.new(
28
+ message:
29
+ OpenStruct.new(
30
+ content: [OpenStruct.new(text: '{"sentiment": "positive"}')]
31
+ )
30
32
  )
31
- )
32
33
  )
33
34
  end
34
35
 
35
36
  before do
36
- allow_any_instance_of(Aws::BedrockRuntime::Client).to receive(:converse).and_return(mock_response)
37
+ allow_any_instance_of(Aws::BedrockRuntime::Client).to receive(
38
+ :converse
39
+ ).and_return(mock_response)
37
40
  end
38
41
 
39
- it 'returns a hash with raw and parsed response' do
42
+ it "returns a hash with raw and parsed response" do
40
43
  response = provider.call(prompt, task)
41
- expect(response).to eq({
42
- raw: '{"sentiment": "positive"}',
43
- parsed: { 'sentiment' => 'positive' }
44
- })
44
+ expect(response).to eq(
45
+ {
46
+ raw: '{"sentiment": "positive"}',
47
+ parsed: {
48
+ "sentiment" => "positive"
49
+ }
50
+ }
51
+ )
45
52
  end
46
53
 
47
- context 'when the response is not valid JSON' do
54
+ context "when the response is not valid JSON" do
48
55
  let(:mock_response) do
49
56
  OpenStruct.new(
50
- output: OpenStruct.new(
51
- message: OpenStruct.new(
52
- content: [
53
- OpenStruct.new(text: 'not valid json')
54
- ]
57
+ output:
58
+ OpenStruct.new(
59
+ message:
60
+ OpenStruct.new(
61
+ content: [OpenStruct.new(text: "not valid json")]
62
+ )
55
63
  )
56
- )
57
64
  )
58
65
  end
59
66
 
60
- it 'returns raw response with nil parsed value' do
67
+ it "returns raw response with nil parsed value" do
61
68
  response = provider.call(prompt, task)
62
- expect(response).to eq({
63
- raw: 'not valid json',
64
- parsed: nil
65
- })
69
+ expect(response).to eq({ raw: "not valid json", parsed: nil })
66
70
  end
67
71
  end
68
72
 
69
- context 'when the response content is nil' do
73
+ context "when the response content is nil" do
70
74
  let(:mock_response) do
71
75
  OpenStruct.new(
72
- output: OpenStruct.new(
73
- message: OpenStruct.new(
74
- content: nil
75
- )
76
- )
76
+ output: OpenStruct.new(message: OpenStruct.new(content: nil))
77
77
  )
78
78
  end
79
79
 
80
- it 'returns nil for both raw and parsed values' do
80
+ it "returns nil for both raw and parsed values" do
81
81
  response = provider.call(prompt, task)
82
- expect(response).to eq({
83
- raw: nil,
84
- parsed: nil
85
- })
82
+ expect(response).to eq({ raw: nil, parsed: nil })
86
83
  end
87
84
  end
88
85
 
89
- context 'when the response content array is empty' do
86
+ context "when the response content array is empty" do
90
87
  let(:mock_response) do
91
88
  OpenStruct.new(
92
- output: OpenStruct.new(
93
- message: OpenStruct.new(
94
- content: []
95
- )
96
- )
89
+ output: OpenStruct.new(message: OpenStruct.new(content: []))
97
90
  )
98
91
  end
99
92
 
100
- it 'returns nil for both raw and parsed values' do
93
+ it "returns nil for both raw and parsed values" do
101
94
  response = provider.call(prompt, task)
102
- expect(response).to eq({
103
- raw: nil,
104
- parsed: nil
105
- })
95
+ expect(response).to eq({ raw: nil, parsed: nil })
106
96
  end
107
97
  end
108
98
  end
109
- end
99
+ end
@@ -1,62 +1,54 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  RSpec.describe Lluminary::Providers::OpenAI do
4
- let(:config) { { api_key: 'test-key' } }
5
+ let(:config) { { api_key: "test-key" } }
5
6
  let(:provider) { described_class.new(**config) }
6
7
 
7
- describe '#client' do
8
- it 'returns the OpenAI client instance' do
8
+ describe "#client" do
9
+ it "returns the OpenAI client instance" do
9
10
  expect(provider.client).to be_a(OpenAI::Client)
10
11
  end
11
12
  end
12
13
 
13
- describe '#call' do
14
- let(:prompt) { 'Test prompt' }
15
- let(:task) { 'Test task' }
14
+ describe "#call" do
15
+ let(:prompt) { "Test prompt" }
16
+ let(:task) { "Test task" }
16
17
  let(:mock_response) do
17
18
  {
18
- 'choices' => [
19
- {
20
- 'message' => {
21
- 'content' => '{"summary": "Test response"}'
22
- }
23
- }
19
+ "choices" => [
20
+ { "message" => { "content" => '{"summary": "Test response"}' } }
24
21
  ]
25
22
  }
26
23
  end
27
24
 
28
25
  before do
29
- allow_any_instance_of(OpenAI::Client).to receive(:chat).and_return(mock_response)
26
+ allow_any_instance_of(OpenAI::Client).to receive(:chat).and_return(
27
+ mock_response
28
+ )
30
29
  end
31
30
 
32
- it 'returns a hash with raw and parsed response' do
31
+ it "returns a hash with raw and parsed response" do
33
32
  response = provider.call(prompt, task)
34
- expect(response).to eq({
35
- raw: '{"summary": "Test response"}',
36
- parsed: { 'summary' => 'Test response' }
37
- })
33
+ expect(response).to eq(
34
+ {
35
+ raw: '{"summary": "Test response"}',
36
+ parsed: {
37
+ "summary" => "Test response"
38
+ }
39
+ }
40
+ )
38
41
  end
39
42
 
40
- context 'when the response is not valid JSON' do
43
+ context "when the response is not valid JSON" do
41
44
  let(:mock_response) do
42
- {
43
- 'choices' => [
44
- {
45
- 'message' => {
46
- 'content' => 'not valid json'
47
- }
48
- }
49
- ]
50
- }
45
+ { "choices" => [{ "message" => { "content" => "not valid json" } }] }
51
46
  end
52
47
 
53
- it 'returns raw response with nil parsed value' do
48
+ it "returns raw response with nil parsed value" do
54
49
  response = provider.call(prompt, task)
55
- expect(response).to eq({
56
- raw: 'not valid json',
57
- parsed: nil
58
- })
50
+ expect(response).to eq({ raw: "not valid json", parsed: nil })
59
51
  end
60
52
  end
61
53
  end
62
- end
54
+ end
@@ -1,14 +1,17 @@
1
- require 'spec_helper'
2
- require 'lluminary/providers/test'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+ require "lluminary/providers/test"
3
4
 
4
5
  RSpec.describe Lluminary::Providers::Test do
5
6
  let(:provider) { described_class.new }
6
7
  let(:prompt) { "Test prompt" }
7
- let(:task_class) { double("TaskClass", output_fields: { summary: { type: :string } }) }
8
+ let(:task_class) do
9
+ double("TaskClass", output_fields: { summary: { type: :string } })
10
+ end
8
11
  let(:task) { double("Task", class: task_class) }
9
12
 
10
- describe '#call' do
11
- it 'returns a hash with raw and parsed response' do
13
+ describe "#call" do
14
+ it "returns a hash with raw and parsed response" do
12
15
  response = provider.call(prompt, task)
13
16
 
14
17
  expect(response).to be_a(Hash)
@@ -16,7 +19,7 @@ RSpec.describe Lluminary::Providers::Test do
16
19
  expect(response[:parsed]).to eq({ "summary" => "Test string value" })
17
20
  end
18
21
 
19
- it 'handles prompts with schema descriptions' do
22
+ it "handles prompts with schema descriptions" do
20
23
  prompt_with_schema = <<~PROMPT
21
24
  Test prompt
22
25
 
@@ -36,22 +39,24 @@ RSpec.describe Lluminary::Providers::Test do
36
39
  expect(response[:parsed]).to eq({ "summary" => "Test string value" })
37
40
  end
38
41
 
39
- it 'generates integer values for integer fields' do
40
- task_class = double("TaskClass", output_fields: { count: { type: :integer } })
42
+ it "generates integer values for integer fields" do
43
+ task_class =
44
+ double("TaskClass", output_fields: { count: { type: :integer } })
41
45
  task = double("Task", class: task_class)
42
-
46
+
43
47
  response = provider.call(prompt, task)
44
48
  expect(response[:raw]).to eq('{"count": 0}')
45
49
  expect(response[:parsed]).to eq({ "count" => 0 })
46
50
  end
47
51
 
48
- it 'raises error for unsupported types' do
49
- task_class = double("TaskClass", output_fields: { value: { type: :unsupported } })
52
+ it "raises error for unsupported types" do
53
+ task_class =
54
+ double("TaskClass", output_fields: { value: { type: :unsupported } })
50
55
  task = double("Task", class: task_class)
51
-
52
- expect {
53
- provider.call(prompt, task)
54
- }.to raise_error("Unsupported type: unsupported")
56
+
57
+ expect { provider.call(prompt, task) }.to raise_error(
58
+ "Unsupported type: unsupported"
59
+ )
55
60
  end
56
61
  end
57
- end
62
+ end
@@ -1,31 +1,38 @@
1
- require 'lluminary'
1
+ # frozen_string_literal: true
2
+ require "lluminary"
2
3
 
3
4
  RSpec.describe Lluminary::Result do
4
5
  let(:raw_response) { "Test response" }
5
6
  let(:output) { { summary: "Test summary" } }
6
7
  let(:prompt) { "Test prompt" }
7
- let(:result) { described_class.new(raw_response: raw_response, output: output, prompt: prompt) }
8
+ let(:result) do
9
+ described_class.new(
10
+ raw_response: raw_response,
11
+ output: output,
12
+ prompt: prompt
13
+ )
14
+ end
8
15
 
9
- describe '#raw_response' do
10
- it 'returns the raw response' do
16
+ describe "#raw_response" do
17
+ it "returns the raw response" do
11
18
  expect(result.raw_response).to eq(raw_response)
12
19
  end
13
20
  end
14
21
 
15
- describe '#output' do
16
- it 'returns an OpenStruct with the output data' do
22
+ describe "#output" do
23
+ it "returns an OpenStruct with the output data" do
17
24
  expect(result.output).to be_a(OpenStruct)
18
25
  expect(result.output.summary).to eq(output[:summary])
19
26
  end
20
27
 
21
- it 'allows accessing output fields as methods' do
28
+ it "allows accessing output fields as methods" do
22
29
  expect(result.output.summary).to eq(output[:summary])
23
30
  end
24
31
  end
25
32
 
26
- describe '#prompt' do
27
- it 'returns the prompt' do
33
+ describe "#prompt" do
34
+ it "returns the prompt" do
28
35
  expect(result.prompt).to eq(prompt)
29
36
  end
30
37
  end
31
- end
38
+ end
@@ -1,11 +1,18 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  RSpec.describe Lluminary::SchemaModel do
4
- describe '.build' do
5
+ describe ".build" do
5
6
  let(:fields) do
6
7
  {
7
- name: { type: :string, description: "The user's name" },
8
- age: { type: :integer, description: "The user's age" }
8
+ name: {
9
+ type: :string,
10
+ description: "The user's name"
11
+ },
12
+ age: {
13
+ type: :integer,
14
+ description: "The user's age"
15
+ }
9
16
  }
10
17
  end
11
18
 
@@ -16,35 +23,39 @@ RSpec.describe Lluminary::SchemaModel do
16
23
  ]
17
24
  end
18
25
 
19
- let(:model_class) { described_class.build(fields: fields, validations: validations) }
26
+ let(:model_class) do
27
+ described_class.build(fields: fields, validations: validations)
28
+ end
20
29
 
21
- it 'creates a class that inherits from SchemaModel' do
30
+ it "creates a class that inherits from SchemaModel" do
22
31
  expect(model_class.ancestors).to include(described_class)
23
32
  end
24
33
 
25
- it 'includes ActiveModel::Validations' do
34
+ it "includes ActiveModel::Validations" do
26
35
  expect(model_class.ancestors).to include(ActiveModel::Validations)
27
36
  end
28
37
 
29
- it 'adds accessors for fields' do
38
+ it "adds accessors for fields" do
30
39
  instance = model_class.new(name: "John", age: 30)
31
40
  expect(instance.name).to eq("John")
32
41
  expect(instance.age).to eq(30)
33
42
  end
34
43
 
35
- it 'validates presence' do
44
+ it "validates presence" do
36
45
  instance = model_class.new(age: 30)
37
46
  expect(instance.valid?).to be false
38
47
  expect(instance.errors.full_messages).to include("Name can't be blank")
39
48
  end
40
49
 
41
- it 'validates numericality' do
50
+ it "validates numericality" do
42
51
  instance = model_class.new(name: "John", age: 0)
43
52
  expect(instance.valid?).to be false
44
- expect(instance.errors.full_messages).to include("Age must be greater than 0")
53
+ expect(instance.errors.full_messages).to include(
54
+ "Age must be greater than 0"
55
+ )
45
56
  end
46
57
 
47
- it 'validates types' do
58
+ it "validates types" do
48
59
  instance = model_class.new(name: 123, age: "30")
49
60
  expect(instance.valid?).to be false
50
61
  expect(instance.errors.full_messages).to include(
@@ -53,34 +64,32 @@ RSpec.describe Lluminary::SchemaModel do
53
64
  )
54
65
  end
55
66
 
56
- it 'validates float types' do
57
- fields = {
58
- price: { type: :float, description: "The price" }
59
- }
67
+ it "validates float types" do
68
+ fields = { price: { type: :float, description: "The price" } }
60
69
  model_class = described_class.build(fields: fields, validations: [])
61
-
70
+
62
71
  # Test that nil is allowed
63
72
  instance = model_class.new(price: nil)
64
73
  expect(instance.valid?).to be true
65
-
74
+
66
75
  # Test invalid float value
67
76
  instance = model_class.new(price: "not a float")
68
77
  expect(instance.valid?).to be false
69
78
  expect(instance.errors.full_messages).to include("Price must be a float")
70
-
79
+
71
80
  # Test valid float value
72
81
  instance = model_class.new(price: 12.34)
73
82
  expect(instance.valid?).to be true
74
83
  end
75
84
 
76
- it 'accepts valid attributes' do
85
+ it "accepts valid attributes" do
77
86
  instance = model_class.new(name: "John", age: 30)
78
87
  expect(instance.valid?).to be true
79
88
  end
80
89
 
81
- it 'provides access to raw attributes' do
90
+ it "provides access to raw attributes" do
82
91
  instance = model_class.new(name: "John", age: 30)
83
92
  expect(instance.attributes).to eq({ "name" => "John", "age" => 30 })
84
93
  end
85
94
  end
86
- end
95
+ end