transcriber 0.0.2 → 0.0.3

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