pipeline_dealers 0.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.
Files changed (60) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +64 -0
  7. data/Rakefile +1 -0
  8. data/examples/companies_create_and_destroy.rb +6 -0
  9. data/examples/companies_list_all.rb +9 -0
  10. data/examples/company_details.rb +19 -0
  11. data/examples/company_notes.rb +17 -0
  12. data/examples/company_people.rb +15 -0
  13. data/examples/config.rb +54 -0
  14. data/examples/find_person.rb +9 -0
  15. data/examples/list_custom_fields.rb +19 -0
  16. data/examples/people_create_and_destroy.rb +6 -0
  17. data/examples/people_list_all.rb +9 -0
  18. data/lib/pipeline_dealers/backend/base/backend.rb +13 -0
  19. data/lib/pipeline_dealers/backend/base/collection.rb +111 -0
  20. data/lib/pipeline_dealers/backend/base.rb +2 -0
  21. data/lib/pipeline_dealers/backend/http/backend.rb +64 -0
  22. data/lib/pipeline_dealers/backend/http/collection.rb +32 -0
  23. data/lib/pipeline_dealers/backend/http/connection.rb +69 -0
  24. data/lib/pipeline_dealers/backend/http/fetcher.rb +78 -0
  25. data/lib/pipeline_dealers/backend/http.rb +5 -0
  26. data/lib/pipeline_dealers/backend/test/backend.rb +34 -0
  27. data/lib/pipeline_dealers/backend/test/collection.rb +34 -0
  28. data/lib/pipeline_dealers/backend/test.rb +3 -0
  29. data/lib/pipeline_dealers/client.rb +23 -0
  30. data/lib/pipeline_dealers/delegator.rb +61 -0
  31. data/lib/pipeline_dealers/error/connection.rb +17 -0
  32. data/lib/pipeline_dealers/error/custom_field.rb +12 -0
  33. data/lib/pipeline_dealers/error/invalid_attribute.rb +29 -0
  34. data/lib/pipeline_dealers/error.rb +12 -0
  35. data/lib/pipeline_dealers/limits.rb +7 -0
  36. data/lib/pipeline_dealers/model/company/custom_field.rb +9 -0
  37. data/lib/pipeline_dealers/model/company.rb +46 -0
  38. data/lib/pipeline_dealers/model/custom_field.rb +94 -0
  39. data/lib/pipeline_dealers/model/has_custom_fields.rb +62 -0
  40. data/lib/pipeline_dealers/model/note.rb +37 -0
  41. data/lib/pipeline_dealers/model/person/custom_field.rb +9 -0
  42. data/lib/pipeline_dealers/model/person.rb +63 -0
  43. data/lib/pipeline_dealers/model.rb +158 -0
  44. data/lib/pipeline_dealers/test.rb +10 -0
  45. data/lib/pipeline_dealers/version.rb +3 -0
  46. data/lib/pipeline_dealers.rb +24 -0
  47. data/pipeline_dealers.gemspec +39 -0
  48. data/spec/acceptance/companies/creation_spec.rb +30 -0
  49. data/spec/acceptance/companies/custom_fields_spec.rb +8 -0
  50. data/spec/acceptance/companies/updating_spec.rb +36 -0
  51. data/spec/acceptance/people/creation_spec.rb +20 -0
  52. data/spec/pipeline_dealers/backend/http/backend_spec.rb +48 -0
  53. data/spec/pipeline_dealers/backend/http/collection_spec.rb +158 -0
  54. data/spec/pipeline_dealers/model/custom_field_spec.rb +129 -0
  55. data/spec/pipeline_dealers/model/has_custom_fields_spec.rb +115 -0
  56. data/spec/pipeline_dealers/model_spec.rb +33 -0
  57. data/spec/pipeline_dealers/test_client_spec.rb +80 -0
  58. data/spec/support/test_model.rb +6 -0
  59. data/todo.md +13 -0
  60. metadata +291 -0
@@ -0,0 +1,158 @@
1
+ require "json"
2
+ require "active_support/hash_with_indifferent_access"
3
+
4
+ module PipelineDealers
5
+ class Model
6
+ class << self
7
+ attr_accessor :collection_url
8
+ attr_accessor :attribute_name
9
+ attr_reader :attributes
10
+
11
+ def attrs *attrs
12
+ if attrs.last.kind_of?(Hash)
13
+ options = attrs.pop
14
+ else
15
+ options = {}
16
+ end
17
+
18
+ attrs.each do |attr|
19
+ attr attr, options
20
+ end
21
+ end
22
+
23
+ def attr name, options = {}
24
+ @attributes ||= {}
25
+ @attributes[name] = options
26
+
27
+ # Getter
28
+ define_method name do
29
+ @attributes[name.to_s]
30
+ end
31
+
32
+ # Setter, only if not read only
33
+ if options[:read_only] != true
34
+ define_method "#{name}=".to_sym do |value|
35
+ @attributes[name.to_s] = value
36
+ end
37
+ end
38
+ end
39
+
40
+ def inherited(klass)
41
+ (@attributes || []).each do |name, options|
42
+ klass.attr(name, options)
43
+ end
44
+ end
45
+ end
46
+
47
+ attr_accessor :attributes
48
+ attr_reader :id
49
+
50
+ def initialize(options = {})
51
+ @options = options
52
+ @persisted = options.delete(:persisted) == true
53
+ @client = options.delete(:client)
54
+ @collection = options.delete(:collection)
55
+
56
+ import_attributes! options.delete(:attributes)
57
+ end
58
+
59
+ def ==(other)
60
+ if other.kind_of?(self.class)
61
+ other.id == self.id && other.attributes == self.attributes
62
+ else
63
+ super(other)
64
+ end
65
+ end
66
+
67
+ def persisted?
68
+ @persisted == true
69
+ end
70
+
71
+ def new_record?
72
+ @persisted == false
73
+ end
74
+
75
+ def save
76
+ @collection.save(self)
77
+ self
78
+ end
79
+
80
+ def destroy
81
+ @collection.destroy(self)
82
+ self
83
+ end
84
+
85
+ IGNORE_ATTRIBUTES_WHEN_SAVING = [:updated_at, :created_at]
86
+ def save_attrs
87
+ # Ignore some attributes
88
+ save_attrs = @attributes.reject { |k, v| IGNORE_ATTRIBUTES_WHEN_SAVING.member?(k) }
89
+
90
+ # And allow a model class to modify / edit attributes even further
91
+ if respond_to?(:attributes_for_saving)
92
+ save_attrs = attributes_for_saving(save_attrs)
93
+ else
94
+ save_attrs
95
+ end
96
+ end
97
+
98
+ def to_s
99
+ "<#{self.class}:#{self.object_id} @attibutes=#{@attributes.inspect}>"
100
+ end
101
+
102
+ protected
103
+
104
+ def import_attributes! attributes
105
+ @attributes = stringify_keys(attributes || {})
106
+ @id = @attributes.delete(:id)
107
+
108
+ # Give subclasses the opportunity to hook in here.
109
+ process_attributes if respond_to?(:process_attributes)
110
+
111
+ check_for_non_existent_attributes!
112
+ end
113
+
114
+ def check_for_non_existent_attributes!
115
+ attribute_keys = @attributes.keys.collect(&:to_sym)
116
+
117
+ invalid_attributes = attribute_keys - valid_attribute_names
118
+
119
+ if invalid_attributes.any?
120
+ raise Error::InvalidAttributeName.new(self.class, invalid_attributes.first)
121
+ end
122
+ end
123
+
124
+ # Recursively converts a hash structure with symbol keys to a hash
125
+ # with indifferent keys
126
+ def stringify_keys original
127
+ result = HashWithIndifferentAccess.new
128
+
129
+ original.each do |key, value|
130
+ result[key] = stringify_value(value)
131
+ end
132
+
133
+ result
134
+ end
135
+
136
+ def valid_attribute_names
137
+ attrs = self.class.instance_variable_get(:@attributes)
138
+ if attrs.nil?
139
+ []
140
+ else
141
+ attrs.keys
142
+ end
143
+ end
144
+
145
+ def stringify_value value
146
+ case value
147
+ when String, Fixnum, NilClass, FalseClass, TrueClass
148
+ return value
149
+ when Hash, HashWithIndifferentAccess
150
+ return stringify_keys(value)
151
+ when Array
152
+ return value.collect { |item| stringify_value(item) }
153
+ else
154
+ raise "Unkown type: #{value.class}"
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,10 @@
1
+ require "pipeline_dealers"
2
+ require "pipeline_dealers/backend/test"
3
+
4
+ module PipelineDealers
5
+ class TestClient < Client
6
+ def initialize(options = {})
7
+ super(options.merge(backend: Backend::Test))
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module PipelineDealers
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ require "pipeline_dealers/version"
2
+
3
+ module PipelineDealers
4
+ require "pipeline_dealers/delegator"
5
+
6
+ require "pipeline_dealers/limits"
7
+ require "pipeline_dealers/error"
8
+ require "pipeline_dealers/client"
9
+
10
+ # Base model
11
+ require "pipeline_dealers/model"
12
+ require "pipeline_dealers/model/custom_field"
13
+ require "pipeline_dealers/model/has_custom_fields"
14
+
15
+ # Models
16
+ require "pipeline_dealers/model/company"
17
+ require "pipeline_dealers/model/company/custom_field"
18
+ require "pipeline_dealers/model/person"
19
+ require "pipeline_dealers/model/person/custom_field"
20
+ require "pipeline_dealers/model/note"
21
+
22
+ require "pipeline_dealers/backend/base"
23
+ require "pipeline_dealers/backend/http"
24
+ end
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pipeline_dealers/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "pipeline_dealers"
8
+ gem.version = PipelineDealers::VERSION
9
+ gem.authors = ["Maarten Hoogendoorn"]
10
+ gem.email = ["maarten@springest.com"]
11
+ gem.description = %q{API client for PipelineDeals}
12
+ gem.summary = gem.description
13
+ gem.homepage = "https://github.com/Springest/pipeline_dealers"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "faraday", "~> 0.8.0"
21
+ gem.add_dependency "faraday_middleware", "~> 0.9.0"
22
+ gem.add_dependency "multi_json", "~> 1.0"
23
+
24
+ gem.add_development_dependency "rspec", ">2"
25
+ gem.add_development_dependency "guard-rspec"
26
+ gem.add_development_dependency "reek"
27
+ gem.add_development_dependency "activesupport", "> 3.0.0"
28
+ gem.add_development_dependency "simplecov"
29
+ gem.add_development_dependency "debugger"
30
+ gem.add_development_dependency "rake"
31
+
32
+
33
+ if RUBY_PLATFORM.include? "linux"
34
+ gem.add_development_dependency "rb-inotify", "~> 0.8.8"
35
+ elsif RUBY_PLATFORM.include? "darwin"
36
+ gem.add_development_dependency "growl"
37
+ gem.add_development_dependency "rb-fsevent"
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ __END__
2
+ describe "Company", :acceptance do
3
+ describe "creation" do
4
+ include AcceptanceHelper
5
+
6
+ context "using client.companies.new" do
7
+ let(:company) { client.companies.new(name: "Meh") }
8
+
9
+ context "with a valid model" do
10
+ before { company.name = "Meh" }
11
+
12
+ context "when saved" do
13
+ it "returns a model" do
14
+ company.save.should be_kind_of PLD::Model::Base
15
+ end
16
+ end
17
+ end
18
+
19
+ context "with an invalid model" do
20
+ before { company.name = nil }
21
+
22
+ context "when saved" do
23
+ it "raises an error" do
24
+ expect { company.save}.to raise_error PipeLineDealer::Error::Connection::Unprocessable
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ __END__
2
+ require "helper"
3
+
4
+ describe "Company", :acceptance do
5
+ describe "custom fields" do
6
+ pending "Can't test this (yet), since it's not accessible via the API."
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ __END__
2
+ require "helper"
3
+
4
+ describe "Company", :acceptance do
5
+ describe "updating" do
6
+ include AcceptanceHelper
7
+ let(:company) { client.companies.create(name: "Old") }
8
+
9
+ context "using client.companies.new" do
10
+ context "with a valid model" do
11
+ before { company.name = "new" }
12
+
13
+ context "when saved" do
14
+ it "returns a model" do
15
+ company.save.should be_kind_of PLD::Model::Base
16
+ end
17
+
18
+ it "has the new name" do
19
+ company.save
20
+ client.companies.find(company.id).name.should == "new"
21
+ end
22
+ end
23
+ end
24
+
25
+ context "with an invalid model" do
26
+ before { company.name = nil }
27
+
28
+ context "when saved" do
29
+ it "raises an error" do
30
+ expect { company.save }.to raise_error PipeLineDealer::Error::Connection::Unprocessable
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ __END__
2
+ require "helper"
3
+
4
+ describe "Person", :acceptance do
5
+ describe "creation" do
6
+ include AcceptanceHelper
7
+
8
+ context "using client.people.new" do
9
+ let(:person) { client.people.new(first_name: "Maarten", last_name: "Hoogendoorn") }
10
+
11
+ before { person.first_name = "MoreTea" }
12
+
13
+ context "when saved" do
14
+ it "returns a model" do
15
+ person.save.should be_kind_of PLD::Model::Base
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,48 @@
1
+ require "pipeline_dealers"
2
+ require "support/test_model"
3
+
4
+ module PipelineDealers::Backend
5
+ describe Http do
6
+ let(:backend) { described_class.new(api_key: "zekret") }
7
+ let(:collection) { Http::Collection.new(backend, client: double("Client"), model_klass: TestModel) }
8
+ let(:connection) { subject.connection }
9
+
10
+ subject { backend }
11
+
12
+ describe "saving a model" do
13
+ context "that is new" do
14
+ let(:model) { collection.new(name: "Springest") }
15
+ after(:each) { model.save }
16
+
17
+ it "uses the POST method" do
18
+ connection.should_receive(:post).and_return([200, {}])
19
+ end
20
+
21
+ it "posts to the correct address" do
22
+ connection.should_receive(:post).with("test_models.json", anything).and_return([200, {}])
23
+ end
24
+
25
+ it "posts the correct attributes" do
26
+ connection.should_receive(:post).with(anything, "moeha" => { "name" => "Springest" }).and_return([200, {}])
27
+ end
28
+ end
29
+
30
+ context "that has been persisted" do
31
+ let(:model) { TestModel.new(collection: collection, persisted: true, attributes: { "id" => 123, name: "Springest"}) }
32
+ after(:each) { model.save }
33
+
34
+ it "uses the PUT method" do
35
+ connection.should_receive(:put).and_return([200, {}])
36
+ end
37
+
38
+ it "posts to the correct address" do
39
+ connection.should_receive(:put).with("test_models/123.json", anything).and_return([200, {}])
40
+ end
41
+
42
+ it "posts the correct attributes" do
43
+ connection.should_receive(:put).with(anything, "moeha" => { "name" => "Springest" }).and_return([200, {}])
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,158 @@
1
+ require "pipeline_dealers"
2
+ require "support/test_model"
3
+
4
+ describe PipelineDealers::Backend::Http::Collection do
5
+ let(:connection) { double("Connection") }
6
+ let(:backend) { double("Backend").tap { |b| b.stub(:connection).and_return(connection) } }
7
+ let(:collection) { described_class.new(backend, client: double("Client"), model_klass: TestModel) }
8
+
9
+ subject { collection }
10
+
11
+ context "transparant pagination" do
12
+ let(:expected_params_a) { ["test_models.json", { page: 1, per_page: 200 } ] }
13
+ let(:expected_params_b) { ["test_models.json", { page: 2, per_page: 200 } ] }
14
+
15
+ let(:response_a) do
16
+ [
17
+ 200, # Status code
18
+ { # 'JSON' body
19
+ "pagination" => {
20
+ "page" => 1,
21
+ "pages" => 2,
22
+ "per_page" => 1,
23
+ "total" => 2,
24
+ "url" => '/resource',
25
+ },
26
+ "entries" => [model_a.attributes]
27
+ }
28
+ ]
29
+ end
30
+
31
+ let(:response_b) do
32
+ [
33
+ 200, # Status code
34
+ { # 'JSON' body
35
+ "pagination" => {
36
+ "page" => 2,
37
+ "pages" => 2,
38
+ "per_page" => 1,
39
+ "total" => 2,
40
+ "url" => '/resource',
41
+ },
42
+ "entries" => [model_b.attributes]
43
+ }
44
+ ]
45
+ end
46
+
47
+ let(:model_a) { TestModel.new(collection: subject, attributes: { name: "Maarten" }) }
48
+ let(:model_b) { TestModel.new(collection: subject, attributes: { name: "Hoogendoorn" }) }
49
+
50
+ it "fetches the correct models" do
51
+ connection.should_receive(:get).ordered.once.with(*expected_params_a).and_return(response_a)
52
+ connection.should_receive(:get).ordered.once.with(*expected_params_b).and_return(response_b)
53
+
54
+ subject.all.to_a.should =~ [model_a, model_b]
55
+ end
56
+
57
+ it "returns records that are not new" do
58
+ connection.should_receive(:get).ordered.once.with(*expected_params_a).and_return(response_a)
59
+ connection.should_receive(:get).ordered.once.with(*expected_params_b).and_return(response_b)
60
+
61
+ a, b = subject.all.to_a
62
+
63
+ a.new_record?.should == false
64
+ b.new_record?.should == false
65
+ end
66
+ end
67
+
68
+ context "limit fetching" do
69
+ let(:expected_params) { ["test_models.json", { page:1, per_page: 1} ] }
70
+
71
+ let(:response) do
72
+ [
73
+ 200, # Status code
74
+ { # 'JSON' body
75
+ "pagination" => {
76
+ "page" => 1,
77
+ "pages" => 2,
78
+ "per_page" => 1,
79
+ "total" => 2,
80
+ "url" => '/resource',
81
+ },
82
+ "entries" => [model_a.attributes]
83
+ }
84
+ ]
85
+ end
86
+
87
+ let(:model_a) { TestModel.new(collection: subject, attributes: { name: "Maarten"}) }
88
+
89
+ it "fetches only the resouces before the limit" do
90
+ connection.should_receive(:get).once.with(*expected_params).and_return(response)
91
+ subject.first.should == model_a
92
+ end
93
+ end
94
+
95
+ describe "cacheing" do
96
+ let(:response) do
97
+ [
98
+ 200, # Status code
99
+ {
100
+ "pagination" => {
101
+ "page" => 1,
102
+ "pages" => 1,
103
+ "per_page" => 1,
104
+ "total" => 1,
105
+ "url" => '/resource',
106
+ },
107
+ "entries" => [{}]
108
+ }
109
+ ]
110
+ end
111
+
112
+ before do
113
+ connection.stub(:get).and_return(response)
114
+ end
115
+
116
+ context "not cached" do
117
+ subject { described_class.new(backend, client: double("Client"), model_klass: TestModel, cached: false) }
118
+
119
+ it "doesn't try to cache the result" do
120
+ connection.should_not_receive(:cache)
121
+ subject.all.to_a
122
+ end
123
+ end
124
+
125
+ context "cached" do
126
+ subject { described_class.new(backend, client: double("Client"), model_klass: TestModel, cached: true) }
127
+
128
+ it "caches the request" do
129
+ backend.should_receive(:cache)
130
+ subject.all
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "building new objects" do
136
+ context "with no initial attributes" do
137
+ subject { collection.new }
138
+ it { should be_kind_of TestModel }
139
+ its(:persisted?) { should == false}
140
+ end
141
+
142
+ context "with initial attributes" do
143
+ context "valid attributes" do
144
+ subject { collection.new(name: "Springest") }
145
+
146
+ its(:name) { should == "Springest" }
147
+ end
148
+
149
+ context "invalid attributes" do
150
+ context "invalid name" do
151
+ it "raises an exception" do
152
+ expect { subject.new(no_such_name: "Springest") }.to raise_error(PipelineDealers::Error::InvalidAttributeName)
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,129 @@
1
+ require "pipeline_dealers"
2
+
3
+ module PipelineDealers
4
+ class Model
5
+ describe CustomField do
6
+ let(:model) { nil }
7
+
8
+ describe "decode" do
9
+ context "numeric" do
10
+ before { subject.field_type = "numeric" }
11
+
12
+ it "decodes correctly" do
13
+ subject.decode(model, 123).should == 123
14
+ end
15
+ end
16
+
17
+ context "text" do
18
+ before { subject.field_type = "text" }
19
+
20
+ it "decodes correctly" do
21
+ subject.decode(model, "Moehaha").should == "Moehaha"
22
+ end
23
+ end
24
+
25
+ context "currency" do
26
+ before { subject.field_type = "currency" }
27
+
28
+ it "decodes correctly" do
29
+ subject.decode(model, 42).should == 42
30
+ end
31
+ end
32
+
33
+ context "dropdown" do
34
+ subject do
35
+ CustomField.new(attributes: {
36
+ "field_type" => "dropdown",
37
+ "custom_field_label_dropdown_entries" => [{"id" => 123, "value" => "My Item"}]
38
+ })
39
+ end
40
+
41
+ it "decodes correctly" do
42
+ subject.decode(model, 123).should == "My Item"
43
+ end
44
+ end
45
+
46
+ context "multi_select" do
47
+ subject do
48
+ CustomField.new(attributes: {
49
+ "field_type" => "multi_select",
50
+ "custom_field_label_dropdown_entries" => [{"id" => 123, "value" => "My Item"}]
51
+ })
52
+ end
53
+
54
+ it "decodes correctly" do
55
+ subject.decode(model, [123]).should == ["My Item"]
56
+ end
57
+ end
58
+
59
+ context "Unkown type" do
60
+ before { subject.field_type = "not-known" }
61
+
62
+ it "throws an exception" do
63
+ expect { subject.decode(model, 42) }.to raise_error
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "encode" do
69
+ context "numeric" do
70
+ before { subject.field_type = "numeric" }
71
+
72
+ it "encodes correctly" do
73
+ subject.encode(model, 123).should == 123
74
+ end
75
+ end
76
+
77
+ context "text" do
78
+ before { subject.field_type = "text" }
79
+
80
+ it "encodes correctly" do
81
+ subject.encode(model, "Moehaha").should == "Moehaha"
82
+ end
83
+ end
84
+
85
+ context "currency" do
86
+ before { subject.field_type = "currency" }
87
+
88
+ it "encodes correctly" do
89
+ subject.encode(model, 42).should == 42
90
+ end
91
+ end
92
+
93
+ context "dropdown" do
94
+ subject do
95
+ CustomField.new(attributes: {
96
+ "field_type" => "dropdown",
97
+ "custom_field_label_dropdown_entries" => [{"id" => 123, "value" => "My Item"}]
98
+ })
99
+ end
100
+
101
+ it "encodes correctly" do
102
+ subject.encode(model, "My Item").should == 123
103
+ end
104
+ end
105
+
106
+ context "multi_select" do
107
+ subject do
108
+ CustomField.new(attributes: {
109
+ "field_type" => "multi_select",
110
+ "custom_field_label_dropdown_entries" => [{"id" => 123, "value" => "My Item"}]
111
+ })
112
+ end
113
+
114
+ it "encodes correctly" do
115
+ subject.encode(model, ["My Item"]).should == [123]
116
+ end
117
+ end
118
+
119
+ context "Unkown type" do
120
+ before { subject.field_type = "not-known" }
121
+
122
+ it "throws an exception" do
123
+ expect { subject.encode(model, 42) }.to raise_error
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end