pipeline_dealers 0.0.1

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