transcriber 0.0.2 → 0.0.3

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.
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  guard 'rspec', cli: '--color' do
2
- watch(%r{spec/spec_helper.rb}) { "spec" }
3
- watch(%r{^config\/(.*\/)*.*\.rb$}) { "spec" }
2
+ watch(%r{spec/spec_helper.rb}) {"spec"}
3
+ watch(%r{^config\/(.*\/)*.*\.rb$}) {"spec"}
4
4
 
5
5
  watch(%r{^spec/.+_spec\.rb})
6
- watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
7
- end
6
+ watch(%r{^lib/(.+)\.rb}) {|m| "spec/#{m[1]}_spec.rb"}
7
+ end
data/README.md CHANGED
@@ -1,13 +1,213 @@
1
1
  # Transcriber
2
2
 
3
+ **Map from one or more input hashes** to instances of your model classes.
3
4
 
4
- ## Installation
5
-
6
- gem install transciber
5
+ **Map to restful responses** from these model instances. (Some hypermedia magic happens here).
7
6
 
8
7
  ## Usage
9
8
 
10
- [See Examples](https://github.com/rodrigues/transcriber/tree/master/examples)
9
+ ### Input hash to model
10
+
11
+ Given you have:
12
+
13
+ input = {
14
+ "result" => [
15
+ {
16
+ "id" => 1938713897398,
17
+ "login" => "jsmith180",
18
+ "username" => "John Smith",
19
+ "personal_info" => {
20
+ "birth_date" => "1999-02-10",
21
+ "registered_date" => "2015-04-05"
22
+ }
23
+ },
24
+ {
25
+ "id" => 92171987391873,
26
+ "login" => "john_appleseed",
27
+ "username" => "John Appleseed",
28
+ "personal_info" => {
29
+ "birth_date" => "1999-02-10",
30
+ "registered_date" => "2015-04-05"
31
+ }
32
+ }
33
+ ]
34
+ }
35
+
36
+ And you want to transform this input in some instances of a `class User; attr_accessor :id, :login, :name, :birth; end`
37
+
38
+ With transcriber you can parse the input to instances of `User`:
39
+
40
+ users = User.parse(input, :result) # starts looking input hash from 'result' key
41
+
42
+ Just define your `User` class like this:
43
+
44
+ class User < Transcriber::Resource
45
+ id
46
+ property :login
47
+ property :name, field: :username # input hash key is different from resource key
48
+ property :birth, field: 'personal_info/birth_date' # digg hash path defined by '/', the last being the key
49
+ end
50
+
51
+ ### Model instances array to restful response hash
52
+
53
+ User.resources(users)
54
+ =>
55
+
56
+ {
57
+ entries: [
58
+ {
59
+ id: 1938713897398,
60
+ login: "jsmith180",
61
+ name: "John Smith",
62
+ birth: "1999-02-10",
63
+ href: "http://app/api/users/1938713897398"
64
+ },
65
+ {
66
+ id: 92171987391873,
67
+ login: "john_appleseed",
68
+ name: "John Appleseed",
69
+ birth: "1999-02-10",
70
+ href: "http://app/api/users/92171987391873"
71
+ }
72
+ ]
73
+ }
74
+
75
+ ### Model instance to restful response hash
76
+
77
+ user.resource
78
+ =>
79
+
80
+ {
81
+ id: 1938713897398,
82
+ login: "jsmith180",
83
+ name: "John Smith",
84
+ birth: "1999-02-10",
85
+ href: "http://app/api/users/1938713897398"
86
+ }
87
+
88
+ ### Input hash to restful response hash
89
+
90
+ User.transcribe(input, :result)
91
+
92
+ ## Embedding other resources
93
+
94
+ ### [`embeds_one`](https://github.com/rodrigues/transcriber/tree/master/examples/embeds_one)
95
+
96
+ class Customer < Transcriber::Resource
97
+ id field: 'cust_id'
98
+ embeds_one :address
99
+ end
100
+
101
+ class Address < Transcriber::Resource
102
+ property :street,
103
+ property :number, field: 'n'
104
+ properties :city, :state, :country
105
+ end
106
+
107
+ {
108
+ id: 1938713897398,
109
+ address: {
110
+ street: 'transcriber st.',
111
+ number: '39',
112
+ city: 'hashland',
113
+ state: 'std',
114
+ country: 'ruby'
115
+ },
116
+ href: "http://app/api/users/1938713897398"
117
+ }
118
+
119
+ ### [`embeds_many`](https://github.com/rodrigues/transcriber/tree/master/examples/embeds_many)
120
+
121
+ class OrderItems < Transcriber::Resource
122
+ property :item_id, id: true # default serialization: String
123
+ property :quantity, type: Integer
124
+ property :unit_price, type: Float
125
+ property :amount, type: Float
126
+ end
127
+
128
+ class Order < Transcriber::Resource
129
+ id
130
+ embeds_many :items, class: OrderItems
131
+ end
132
+
133
+ {
134
+ id: 739819813719387,
135
+ items: [
136
+ {
137
+ item_id: 1738917139871,
138
+ quantity: 18,
139
+ unit_price: 0.2,
140
+ amount: 3.6
141
+ },
142
+ {
143
+ item_id: 3987187398782,
144
+ quantity: 1,
145
+ unit_price: 39.9,
146
+ amount: 39.9
147
+ }
148
+ ]
149
+ }
150
+
151
+ ## Relations
152
+
153
+ ### `has_one`, `has_many` and `belongs_to`
154
+
155
+ class User < Transcriber::Resource
156
+ id
157
+ property :login
158
+ has_one :avatar
159
+ has_many :achievements
160
+ belongs_to :guilda
161
+ end
162
+
163
+ {
164
+ id: 19837139879,
165
+ login: 'jsmith180',
166
+ links: [
167
+ {
168
+ rel: 'avatar',
169
+ href: 'http://app/api/users/19837139879/avatar'
170
+ },
171
+ {
172
+ rel: 'achievements',
173
+ href: 'http://app/api/users/19837139879/achievements'
174
+ },
175
+ {
176
+ rel: 'guilda',
177
+ href: 'http://app/api/users/19837139879/guilda
178
+ }
179
+ ]
180
+ }
181
+
182
+ ### embedding relations
183
+
184
+ If you want your api to handle `http://app/api/users/19371897318937?include[]=avatar&include[]=guilda`
185
+
186
+ user = User.parse(input, include: [avatar_input, guilda_input]).first
187
+
188
+ {
189
+ id: 19837139879,
190
+ login: 'jsmith180',
191
+ avatar: {
192
+ name: 'K1ll3r',
193
+ specie: 'WTF'
194
+ },
195
+ guilda: {
196
+ name: 'K1ll3rs'
197
+ },
198
+ links: [
199
+ {
200
+ rel: 'achievements',
201
+ href: 'http://app/api/users/19837139879/achievements'
202
+ }
203
+ ]
204
+ }
205
+
206
+ ### [See more examples here](https://github.com/rodrigues/transcriber/tree/master/examples).
207
+
208
+ ## Installation
209
+
210
+ gem install transcriber
11
211
 
12
212
  ## Maintainers
13
213
 
data/examples/all.rb CHANGED
@@ -11,4 +11,4 @@ Dir["examples/*/**.rb"].each do |file|
11
11
  puts
12
12
  puts
13
13
  load file
14
- end
14
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ $:.push 'lib'; require 'transcriber'
4
+
5
+ class Root < Transcriber::Resource
6
+ property :key, id: true
7
+
8
+ property :locale, visible: false
9
+
10
+ embeds_many :names, class_name: 'Hash', visible: false
11
+ property :name, values: proc {names[locale]}
12
+ end
13
+
14
+
15
+ @root = Root.parse({'locale' => 'pt-BR',
16
+ 'key' => 19190839,
17
+ 'names' => {
18
+ 'en' => 'Woot',
19
+ 'pt-BR' => 'Úia',
20
+ 'es' => 'Me gusta'
21
+ }}).first
22
+
23
+ puts "root: #{@root.inspect}"
24
+ puts "resource: #{@root.resource}"
@@ -8,7 +8,7 @@ class Root < Transcriber::Resource
8
8
  embeds_many :items
9
9
  end
10
10
 
11
- root = Root.parse({"items" => [{"id" => 2000}]}).first
11
+ @root = Root.parse({"items" => [{"id" => 2000}]}).first
12
12
 
13
- puts "root: #{root.inspect}"
14
- puts "resource: #{root.resource}"
13
+ puts "root: #{@root.inspect}"
14
+ puts "resource: #{@root.resource}"
@@ -10,7 +10,8 @@ module Transcriber
10
10
  def initialize(name, options = {})
11
11
  super
12
12
  @serializer = options.fetch(:type, Serialization::String)
13
- @translations = options[:values].try(:invert)
13
+ @translations = options[:values] if options[:values].kind_of?(Proc)
14
+ @translations = options[:values].invert if options[:values].kind_of?(Hash)
14
15
  end
15
16
  end
16
17
  end
@@ -16,7 +16,7 @@ module Transcriber
16
16
  !has?
17
17
  end
18
18
 
19
- def parse(value)
19
+ def parse(value, resource = nil)
20
20
  nil
21
21
  end
22
22
  end
@@ -15,6 +15,15 @@ module Transcriber
15
15
  resource.instance_eval &options[:if]
16
16
  end
17
17
 
18
+ def visible?(resource)
19
+ if present? resource
20
+ return true if options[:visible].nil?
21
+ return options[:visible] unless options[:visible].kind_of?(Proc)
22
+ return resource.instance_eval &options[:visible]
23
+ end
24
+ false
25
+ end
26
+
18
27
  def input_path
19
28
  return @input_path if @input_path
20
29
  path = InputPath.resolve(options, convert_input_keys)
@@ -2,10 +2,16 @@ module Transcriber
2
2
  class Resource
3
3
  module Parser
4
4
  module Embeddable
5
- def parse(value)
6
- parsed_value = resource_class.parse(value)
5
+ def parse(value, resource = nil)
6
+ parsed_value = parse? ? resource_class.parse(value) : value
7
7
  one? ? parsed_value.first : parsed_value
8
8
  end
9
+
10
+ private
11
+
12
+ def parse?
13
+ ![Hash, Array].include?(resource_class)
14
+ end
9
15
  end
10
16
  end
11
17
  end
@@ -2,12 +2,19 @@ module Transcriber
2
2
  class Resource
3
3
  module Parser
4
4
  module Property
5
- def parse(value)
6
- translate serializer.serialize(value)
5
+ def parse(value, resource = nil)
6
+ translate(serializer.serialize(value), resource)
7
7
  end
8
8
 
9
- def translate(value)
10
- translations ? translations[value] : value
9
+ def translate(value, resource)
10
+ case translations
11
+ when nil
12
+ value
13
+ when Hash
14
+ translations[value]
15
+ when Proc
16
+ resource.instance_eval &translations
17
+ end
11
18
  end
12
19
  end
13
20
  end
@@ -16,7 +16,7 @@ module Transcriber
16
16
  self.new.tap do |resource|
17
17
  keys.each do |key|
18
18
  value = digg(item, key.input_path)
19
- resource.__send__("#{key.name}=", key.parse(value)) if key.present?(resource)
19
+ resource.__send__("#{key.name}=", key.parse(value, resource)) if key.present?(resource)
20
20
  end
21
21
  end
22
22
  end
@@ -3,7 +3,7 @@ module Transcriber
3
3
  module Response
4
4
  module Embeddable
5
5
  def to_resource(parent)
6
- return {} unless present?(parent)
6
+ return {} unless visible?(parent)
7
7
  embedded = parent.__send__(name)
8
8
  resource = one? ? embedded.resource : embedded.map(&:resource)
9
9
  {name => resource}
@@ -3,7 +3,7 @@ module Transcriber
3
3
  module Response
4
4
  module Property
5
5
  def to_resource(parent)
6
- present?(parent) ? {name => parent.__send__(name)} : {}
6
+ visible?(parent) ? {name => parent.__send__(name)} : {}
7
7
  end
8
8
  end
9
9
  end
@@ -4,7 +4,7 @@ module Transcriber
4
4
  module Relation
5
5
  def to_resource(parent)
6
6
  included = parent.__send__(name)
7
- return {} unless present?(parent) and included
7
+ return {} unless visible?(parent) and included
8
8
  {name => resource_from_included(included)}
9
9
  end
10
10
 
@@ -6,6 +6,7 @@ module Transcriber
6
6
  {entries: model.map(&:resource)}
7
7
  : model.resource
8
8
  end
9
+ alias :resources :normalize
9
10
  end
10
11
  end
11
12
  end
@@ -40,6 +40,10 @@ module Transcriber
40
40
  self.class.relations.map {|key| key.to_relation(self)}
41
41
  end
42
42
 
43
+ def self.transcribe(input, options = {})
44
+ normalize(parse(input, options), options)
45
+ end
46
+
43
47
  def self.method_added(method_name)
44
48
  return unless not_allowed_names.include?(method_name.to_s)
45
49
  puts "warning: redefining '#{method_name}' may cause serious problems"
@@ -1,3 +1,3 @@
1
1
  module Transcriber
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -16,7 +16,60 @@ describe Resource::Key do
16
16
  end
17
17
  end
18
18
 
19
- describe "present?" do
19
+ describe "#visible?" do
20
+ context "when :visible option was defined" do
21
+ context "and :if option was defined with false" do
22
+ subject {Resource::Property.new(name, {if: proc {false}, visible: true})}
23
+ it "return false even when visible is true" do
24
+ subject.visible?(nil).should be_false
25
+ end
26
+ end
27
+
28
+ context "and it evaluates to false" do
29
+ subject {Resource::Property.new(name, {visible: false})}
30
+
31
+ it "returns false" do
32
+ subject.visible?(nil).should be_false
33
+ end
34
+ end
35
+
36
+ context "and it evaluates to true" do
37
+ subject {Resource::Property.new(name, {visible: true})}
38
+
39
+ it "returns true" do
40
+ subject.visible?(nil).should be_true
41
+ end
42
+ end
43
+
44
+ context "with a proc" do
45
+ context "and it evaluates true" do
46
+ subject {Resource::Property.new(name, {visible: proc {true}})}
47
+ it "returns true" do
48
+ subject.visible?("a").should be_true
49
+ end
50
+ end
51
+
52
+ context "and it evaluates false" do
53
+ subject {Resource::Property.new(name, {visible: proc {false}})}
54
+ it "returns false" do
55
+ subject.visible?("a").should be_false
56
+ end
57
+ end
58
+
59
+ context "and it depends on resource instance" do
60
+ subject {Resource::Property.new(name, {visible: proc {show}})}
61
+
62
+ it "uses it" do
63
+ resource = OpenStruct.new
64
+ resource.show = true
65
+ subject.visible?(resource).should be_true
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "#present?" do
20
73
  context "when :if option was defined with a proc" do
21
74
  context "and it evaluates to false" do
22
75
  subject {Resource::Property.new(name, {if: proc {false}})}
@@ -17,9 +17,9 @@ describe Resource::Response::Embeddable do
17
17
  subject.to_resource(example).should == result
18
18
  end
19
19
 
20
- context "when this key shouldn't be present on resource" do
20
+ context "when this key shouldn't be visible on resource" do
21
21
  it "returns an empty hash" do
22
- subject.should_receive(:present?).and_return false
22
+ subject.should_receive(:visible?).and_return false
23
23
  subject.to_resource(example).should == {}
24
24
  end
25
25
  end
@@ -12,9 +12,9 @@ describe Resource::Response::Property do
12
12
  subject.to_resource(customer).should == {login: "jackiechan2010"}
13
13
  end
14
14
 
15
- context "when this key shouldn't be present on resource" do
15
+ context "when this key shouldn't be visible on resource" do
16
16
  it "returns an empty hash" do
17
- subject.should_receive(:present?).and_return false
17
+ subject.should_receive(:visible?).and_return false
18
18
  customer = Customer.new.tap {|c| c.login = "jackiechan2010"}
19
19
  subject.to_resource(customer).should == {}
20
20
  end
@@ -9,9 +9,9 @@ describe Resource::Response do
9
9
  end
10
10
 
11
11
  context "#to_resource" do
12
- context "when the key shouldn't be present on resource" do
12
+ context "when the key shouldn't be visible on resource" do
13
13
  it "returns an empty hash" do
14
- subject.should_receive(:present?).and_return false
14
+ subject.should_receive(:visible?).and_return false
15
15
  subject.to_resource(example).should == {}
16
16
  end
17
17
  end
metadata CHANGED
@@ -1,104 +1,100 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: transcriber
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
4
5
  prerelease:
5
- version: 0.0.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Victor Rodrigues
9
9
  - William Yokoi
10
- - "Guilherme Guimar\xC3\xA3es Filho"
10
+ - Guilherme Guimarães Filho
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
-
15
- date: 2011-08-11 00:00:00 -03:00
16
- default_executable:
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
14
+ date: 2011-08-15 00:00:00.000000000Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
19
17
  name: activesupport
20
- prerelease: false
21
- requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirement: &70342384534920 !ruby/object:Gem::Requirement
22
19
  none: false
23
- requirements:
20
+ requirements:
24
21
  - - ~>
25
- - !ruby/object:Gem::Version
26
- version: "3.0"
22
+ - !ruby/object:Gem::Version
23
+ version: '3.0'
27
24
  type: :runtime
28
- version_requirements: *id001
29
- - !ruby/object:Gem::Dependency
30
- name: i18n
31
25
  prerelease: false
32
- requirement: &id002 !ruby/object:Gem::Requirement
26
+ version_requirements: *70342384534920
27
+ - !ruby/object:Gem::Dependency
28
+ name: i18n
29
+ requirement: &70342384534420 !ruby/object:Gem::Requirement
33
30
  none: false
34
- requirements:
31
+ requirements:
35
32
  - - ~>
36
- - !ruby/object:Gem::Version
37
- version: "0.6"
33
+ - !ruby/object:Gem::Version
34
+ version: '0.6'
38
35
  type: :runtime
39
- version_requirements: *id002
40
- - !ruby/object:Gem::Dependency
41
- name: rspec
42
36
  prerelease: false
43
- requirement: &id003 !ruby/object:Gem::Requirement
37
+ version_requirements: *70342384534420
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &70342384533960 !ruby/object:Gem::Requirement
44
41
  none: false
45
- requirements:
42
+ requirements:
46
43
  - - ~>
47
- - !ruby/object:Gem::Version
48
- version: "2.6"
44
+ - !ruby/object:Gem::Version
45
+ version: '2.6'
49
46
  type: :development
50
- version_requirements: *id003
51
- - !ruby/object:Gem::Dependency
52
- name: guard
53
47
  prerelease: false
54
- requirement: &id004 !ruby/object:Gem::Requirement
48
+ version_requirements: *70342384533960
49
+ - !ruby/object:Gem::Dependency
50
+ name: guard
51
+ requirement: &70342384533500 !ruby/object:Gem::Requirement
55
52
  none: false
56
- requirements:
53
+ requirements:
57
54
  - - ~>
58
- - !ruby/object:Gem::Version
59
- version: "0.5"
55
+ - !ruby/object:Gem::Version
56
+ version: '0.5'
60
57
  type: :development
61
- version_requirements: *id004
62
- - !ruby/object:Gem::Dependency
63
- name: guard-rspec
64
58
  prerelease: false
65
- requirement: &id005 !ruby/object:Gem::Requirement
59
+ version_requirements: *70342384533500
60
+ - !ruby/object:Gem::Dependency
61
+ name: guard-rspec
62
+ requirement: &70342384533040 !ruby/object:Gem::Requirement
66
63
  none: false
67
- requirements:
64
+ requirements:
68
65
  - - ~>
69
- - !ruby/object:Gem::Version
70
- version: "0.4"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.4'
71
68
  type: :development
72
- version_requirements: *id005
73
- - !ruby/object:Gem::Dependency
74
- name: growl
75
69
  prerelease: false
76
- requirement: &id006 !ruby/object:Gem::Requirement
70
+ version_requirements: *70342384533040
71
+ - !ruby/object:Gem::Dependency
72
+ name: growl
73
+ requirement: &70342384532580 !ruby/object:Gem::Requirement
77
74
  none: false
78
- requirements:
75
+ requirements:
79
76
  - - ~>
80
- - !ruby/object:Gem::Version
81
- version: "1.0"
77
+ - !ruby/object:Gem::Version
78
+ version: '1.0'
82
79
  type: :development
83
- version_requirements: *id006
84
- description: ""
85
- email:
80
+ prerelease: false
81
+ version_requirements: *70342384532580
82
+ description: ''
83
+ email:
86
84
  - victorcrodrigues@gmail.com
87
85
  - thekina@gmail.com
88
86
  - guilherme.filho@locaweb.com.br
89
87
  executables: []
90
-
91
88
  extensions: []
92
-
93
89
  extra_rdoc_files: []
94
-
95
- files:
90
+ files:
96
91
  - .gitignore
97
92
  - Gemfile
98
93
  - Guardfile
99
94
  - README.md
100
95
  - Rakefile
101
96
  - examples/all.rb
97
+ - examples/embeds_many/invisible.rb
102
98
  - examples/embeds_many/simple.rb
103
99
  - examples/embeds_many/with_class_name.rb
104
100
  - examples/embeds_many/with_if.rb
@@ -173,35 +169,31 @@ files:
173
169
  - spec/unit/resource_spec.rb
174
170
  - spec/unit/response_spec.rb
175
171
  - transcriber.gemspec
176
- has_rdoc: true
177
172
  homepage: http://github.com/rodrigues/transcriber
178
173
  licenses: []
179
-
180
174
  post_install_message:
181
175
  rdoc_options: []
182
-
183
- require_paths:
176
+ require_paths:
184
177
  - lib
185
- required_ruby_version: !ruby/object:Gem::Requirement
178
+ required_ruby_version: !ruby/object:Gem::Requirement
186
179
  none: false
187
- requirements:
188
- - - ">="
189
- - !ruby/object:Gem::Version
190
- version: "0"
191
- required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ! '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
185
  none: false
193
- requirements:
194
- - - ">="
195
- - !ruby/object:Gem::Version
196
- version: "0"
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
197
190
  requirements: []
198
-
199
191
  rubyforge_project:
200
- rubygems_version: 1.6.2
192
+ rubygems_version: 1.8.6
201
193
  signing_key:
202
194
  specification_version: 3
203
- summary: ""
204
- test_files:
195
+ summary: ''
196
+ test_files:
205
197
  - spec/integration/has_many_spec.rb
206
198
  - spec/integration/property_spec.rb
207
199
  - spec/spec_helper.rb