transcriber 0.0.1 → 0.0.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 (92) hide show
  1. data/.gitignore +1 -0
  2. data/Guardfile +7 -0
  3. data/README.md +39 -0
  4. data/examples/all.rb +14 -0
  5. data/examples/embeds_many/simple.rb +14 -0
  6. data/examples/embeds_many/with_class_name.rb +14 -0
  7. data/examples/embeds_many/with_if.rb +21 -0
  8. data/examples/embeds_many/with_start_key.rb +17 -0
  9. data/examples/embeds_one/simple.rb +14 -0
  10. data/examples/embeds_one/with_class_name.rb +14 -0
  11. data/examples/embeds_one/with_if.rb +21 -0
  12. data/examples/embeds_one/with_start_key.rb +14 -0
  13. data/examples/has_many/simple.rb +29 -0
  14. data/examples/properties/simple.rb +10 -0
  15. data/examples/properties/with_field.rb +10 -0
  16. data/examples/properties/with_field_path.rb +10 -0
  17. data/examples/properties/with_id.rb +10 -0
  18. data/examples/properties/with_if.rb +19 -0
  19. data/examples/properties/with_key_converter.rb +20 -0
  20. data/lib/transcriber/configuration.rb +13 -0
  21. data/lib/transcriber/resource/builder/embeddables.rb +21 -0
  22. data/lib/transcriber/resource/builder/keys.rb +20 -0
  23. data/lib/transcriber/resource/builder/properties.rb +29 -0
  24. data/lib/transcriber/resource/builder/relations.rb +26 -0
  25. data/lib/transcriber/resource/builder/summarizations.rb +14 -0
  26. data/lib/transcriber/resource/builder.rb +10 -0
  27. data/lib/transcriber/resource/input_path.rb +19 -0
  28. data/lib/transcriber/resource/key/association.rb +29 -0
  29. data/lib/transcriber/resource/key/embeddable.rb +8 -0
  30. data/lib/transcriber/resource/key/property.rb +17 -0
  31. data/lib/transcriber/resource/key/relation.rb +24 -0
  32. data/lib/transcriber/resource/key.rb +29 -0
  33. data/lib/transcriber/resource/parser/embeddable.rb +12 -0
  34. data/lib/transcriber/resource/parser/property.rb +15 -0
  35. data/lib/transcriber/resource/parser.rb +21 -18
  36. data/lib/transcriber/resource/response/embeddable.rb +14 -0
  37. data/lib/transcriber/resource/response/property.rb +11 -0
  38. data/lib/transcriber/resource/response/relation.rb +39 -0
  39. data/lib/transcriber/resource/response.rb +11 -0
  40. data/lib/transcriber/resource/serialization/boolean.rb +32 -9
  41. data/lib/transcriber/resource/serialization/date.rb +7 -7
  42. data/lib/transcriber/resource/serialization/float.rb +9 -5
  43. data/lib/transcriber/resource/serialization/integer.rb +13 -0
  44. data/lib/transcriber/resource/serialization/string.rb +7 -5
  45. data/lib/transcriber/resource.rb +34 -14
  46. data/lib/transcriber/version.rb +1 -1
  47. data/lib/transcriber.rb +38 -2
  48. data/spec/integration/has_many_spec.rb +9 -0
  49. data/spec/integration/property_spec.rb +0 -0
  50. data/spec/spec_helper.rb +22 -1
  51. data/spec/support/examples.rb +21 -0
  52. data/spec/support/out.rb +17 -0
  53. data/spec/unit/configuration_spec.rb +24 -0
  54. data/spec/unit/input_path_spec.rb +68 -0
  55. data/spec/unit/key_spec.rb +48 -0
  56. data/spec/{resource → unit}/parser_spec.rb +16 -21
  57. data/spec/unit/resource/builder/embeddables_spec.rb +27 -0
  58. data/spec/unit/resource/builder/properties_spec.rb +154 -0
  59. data/spec/unit/resource/builder/relations_spec.rb +54 -0
  60. data/spec/unit/resource/builder/shared_example_for_association.rb +20 -0
  61. data/spec/unit/resource/builder/summarize_spec.rb +0 -0
  62. data/spec/unit/resource/key/association_spec.rb +69 -0
  63. data/spec/unit/resource/key/embeddable_spec.rb +7 -0
  64. data/spec/unit/resource/key/property_spec.rb +39 -0
  65. data/spec/unit/resource/key/relation_spec.rb +71 -0
  66. data/spec/unit/resource/parser/embeddable_spec.rb +38 -0
  67. data/spec/unit/resource/parser/property_spec.rb +31 -0
  68. data/spec/unit/resource/response/embeddable_spec.rb +55 -0
  69. data/spec/unit/resource/response/property_spec.rb +22 -0
  70. data/spec/unit/resource/response/relation_spec.rb +105 -0
  71. data/spec/unit/resource/serialization/boolean_spec.rb +25 -0
  72. data/spec/unit/resource/serialization/date_spec.rb +13 -0
  73. data/spec/unit/resource/serialization/float_spec.rb +13 -0
  74. data/spec/unit/resource/serialization/integer_spec.rb +13 -0
  75. data/spec/unit/resource/serialization/string_spec.rb +11 -0
  76. data/spec/unit/resource_spec.rb +46 -0
  77. data/spec/unit/response_spec.rb +44 -0
  78. data/transcriber.gemspec +7 -3
  79. metadata +146 -22
  80. data/lib/transcriber/resource/embeddables/embeddable.rb +0 -31
  81. data/lib/transcriber/resource/embeddables/parser.rb +0 -10
  82. data/lib/transcriber/resource/embeddables/resource.rb +0 -9
  83. data/lib/transcriber/resource/embeddables.rb +0 -18
  84. data/lib/transcriber/resource/properties/parser.rb +0 -9
  85. data/lib/transcriber/resource/properties/property.rb +0 -30
  86. data/lib/transcriber/resource/properties/resource.rb +0 -7
  87. data/lib/transcriber/resource/properties.rb +0 -8
  88. data/lib/transcriber/resource/responses.rb +0 -13
  89. data/lib/transcriber/resource/serialization.rb +0 -10
  90. data/spec/resource/embeddables_spec.rb +0 -228
  91. data/spec/resource/properties_spec.rb +0 -69
  92. data/spec/resource_spec.rb +0 -27
@@ -1,26 +1,29 @@
1
- class Transcriber::Resource
2
- module Parser
3
- def parse(input, options = {})
4
- entries(input, options).collect {|item| parse_one(item)}
5
- end
1
+ module Transcriber
2
+ class Resource
3
+ module Parser
4
+ def parse(input, options = {})
5
+ prepare_entries(input, options).collect &method(:parse_item)
6
+ end
6
7
 
7
- private
8
+ private
8
9
 
9
- def entries(input, options)
10
- start_key = options[:start_key]
11
- entries = input
12
- if entries.kind_of?(Hash)
13
- entries = entries[start_key.to_s.upcase] if start_key
14
- entries = [entries]
10
+ def prepare_entries(input, options)
11
+ path = InputPath.resolve(options, Transcriber.configuration.convert_input_keys)
12
+ Array.wrap digg(input, path)
13
+ end
14
+
15
+ def parse_item(item)
16
+ self.new.tap do |resource|
17
+ keys.each do |key|
18
+ value = digg(item, key.input_path)
19
+ resource.__send__("#{key.name}=", key.parse(value)) if key.present?(resource)
20
+ end
21
+ end
15
22
  end
16
- entries
17
- end
18
23
 
19
- def parse_one(item)
20
- params = keys.inject({}) do |buffer, key|
21
- buffer.merge key.name => key.parse(item)
24
+ def digg(input, path)
25
+ path.inject(input) {|buffer, key| buffer = buffer[key]}
22
26
  end
23
- self.new(params)
24
27
  end
25
28
  end
26
29
  end
@@ -0,0 +1,14 @@
1
+ module Transcriber
2
+ class Resource
3
+ module Response
4
+ module Embeddable
5
+ def to_resource(parent)
6
+ return {} unless present?(parent)
7
+ embedded = parent.__send__(name)
8
+ resource = one? ? embedded.resource : embedded.map(&:resource)
9
+ {name => resource}
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Transcriber
2
+ class Resource
3
+ module Response
4
+ module Property
5
+ def to_resource(parent)
6
+ present?(parent) ? {name => parent.__send__(name)} : {}
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ module Transcriber
2
+ class Resource
3
+ module Response
4
+ module Relation
5
+ def to_resource(parent)
6
+ included = parent.__send__(name)
7
+ return {} unless present?(parent) and included
8
+ {name => resource_from_included(included)}
9
+ end
10
+
11
+ def to_relation(parent)
12
+ {:rel => name, :href => href(parent)}
13
+ end
14
+
15
+ private
16
+
17
+ def relation_name(parent)
18
+ relation = parent.class.relations.find do |key|
19
+ key.resource_class == self.resource_class
20
+ end
21
+ relation.name or fail
22
+ end
23
+
24
+ def resource_from_included(included)
25
+ options = {root: false}
26
+ one? ?
27
+ included.resource(options)
28
+ : included.map {|r| r.resource(options)}
29
+ end
30
+
31
+ def href(parent)
32
+ has? ?
33
+ "/#{parent.class.resource_name}/#{parent.id}/#{relation_name(parent)}"
34
+ : "/#{relation_name(parent).to_s.pluralize}/#{parent.id}/#{parent.class.resource_name}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ module Transcriber
2
+ class Resource
3
+ module Response
4
+ def normalize(model, options = {})
5
+ model.kind_of?(Enumerable) ?
6
+ {entries: model.map(&:resource)}
7
+ : model.resource
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,13 +1,36 @@
1
- class Transcriber::Resource
2
- module Serialization
3
- class Boolean
4
- MAPPINGS = {true => [true, "true", "TRUE", "1", 1, 1.0, "x", "X", "t", "T"],
5
- false => [false, "false", "FALSE", "0", 0, 0.0, "", " ", "f", "F", nil]}
1
+ module Transcriber
2
+ class Resource
3
+ module Serialization
4
+ class Boolean
5
+ MAPPINGS = {
6
+ true => true,
7
+ "true" => true,
8
+ "TRUE" => true,
9
+ "1" => true,
10
+ 1 => true,
11
+ 1.0 => true,
12
+ "x" => true,
13
+ "X" => true,
14
+ "t" => true,
15
+ "T" => true,
16
+ false => false,
17
+ "false" => false,
18
+ "FALSE" => false,
19
+ "0" => false,
20
+ 0 => false,
21
+ 0.0 => false,
22
+ "" => false,
23
+ " " => false,
24
+ "f" => false,
25
+ "F" => false,
26
+ nil => false
27
+ }.freeze
6
28
 
7
- def self.serialize(value)
8
- return true if MAPPINGS[true].include?(value)
9
- return false if MAPPINGS[false].include?(value)
10
- raise "value not serializable: #{{value: value, mappings: MAPPINGS}}"
29
+ def self.serialize(value)
30
+ MAPPINGS[value].tap do |bool|
31
+ fail "value not serializable: #{value}" if bool.nil?
32
+ end
33
+ end
11
34
  end
12
35
  end
13
36
  end
@@ -1,11 +1,11 @@
1
- class Transcriber::Resource
2
- module Serialization
3
- class Date
4
- def self.serialize(value)
5
- begin
1
+ module Transcriber
2
+ class Resource
3
+ module Serialization
4
+ class Date
5
+ def self.serialize(value)
6
6
  ::Date.parse value
7
- rescue
8
- raise "value not serializable: #{{value: value}}"
7
+ rescue ArgumentError
8
+ raise "value not serializable: #{value}"
9
9
  end
10
10
  end
11
11
  end
@@ -1,8 +1,12 @@
1
- class Transcriber::Resource
2
- module Serialization
3
- class Float
4
- def self.serialize(value)
5
- value.to_f
1
+ module Transcriber
2
+ class Resource
3
+ module Serialization
4
+ class Float
5
+ def self.serialize(value)
6
+ Float(value)
7
+ rescue ArgumentError
8
+ raise "value not serializable: #{value}"
9
+ end
6
10
  end
7
11
  end
8
12
  end
@@ -0,0 +1,13 @@
1
+ module Transcriber
2
+ class Resource
3
+ module Serialization
4
+ class Integer
5
+ def self.serialize(value)
6
+ Integer(value)
7
+ rescue ArgumentError
8
+ raise "value not serializable: #{value}"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,10 @@
1
- class Transcriber::Resource
2
- module Serialization
3
- class String
4
- def self.serialize(value)
5
- "#{value}"
1
+ module Transcriber
2
+ class Resource
3
+ module Serialization
4
+ class String
5
+ def self.serialize(value)
6
+ value.nil? ? nil : value.to_s
7
+ end
6
8
  end
7
9
  end
8
10
  end
@@ -1,18 +1,13 @@
1
1
  module Transcriber
2
2
  class Resource
3
- autoload :Serialization, 'transcriber/resource/serialization'
4
- autoload :Properties, 'transcriber/resource/properties'
5
- autoload :Property, 'transcriber/resource/properties/property'
6
- autoload :Embeddables, 'transcriber/resource/embeddables'
7
- autoload :Embeddable, 'transcriber/resource/embeddables/embeddable'
8
- autoload :Parser, 'transcriber/resource/parser'
9
- autoload :Responses, 'transcriber/resource/responses'
10
-
11
- extend Properties
12
- extend Embeddables
3
+ extend Builder
13
4
  extend Parser
5
+ extend Response
14
6
  include Serialization
15
7
 
8
+ cattr_accessor :id_key
9
+ cattr_writer :resource_name
10
+
16
11
  def initialize(attrs = {})
17
12
  attrs.map {|name, value| send("#{name}=", value)}
18
13
  end
@@ -21,12 +16,37 @@ module Transcriber
21
16
  @keys ||= []
22
17
  end
23
18
 
24
- def self.resources(entries)
25
- {entries: entries.map(&:resource)}
19
+ def self.relations
20
+ @keys.find_all {|k| k.kind_of?(Relation)}
21
+ end
22
+
23
+ def self.resource_name(custom_name = nil)
24
+ @resource_name ||= custom_name or to_s.demodulize.tableize
25
+ end
26
+
27
+ def resource_id
28
+ __send__(id_key.name)
29
+ end
30
+
31
+ def resource(options = {})
32
+ root = options.fetch(:root, true)
33
+ {}.tap do |resource|
34
+ self.class.keys.inject(resource) {|buffer, key| buffer.merge!(key.to_resource(self))}
35
+ resource.merge!({links: links}) if root and self.class.relations.any?
36
+ end
37
+ end
38
+
39
+ def links
40
+ self.class.relations.map {|key| key.to_relation(self)}
41
+ end
42
+
43
+ def self.method_added(method_name)
44
+ return unless not_allowed_names.include?(method_name.to_s)
45
+ puts "warning: redefining '#{method_name}' may cause serious problems"
26
46
  end
27
47
 
28
- def resource
29
- self.class.keys.inject({}) {|buffer, key| buffer.merge key.to_resource(self)}
48
+ def self.not_allowed_names
49
+ ["resource_id", "resource", "links"]
30
50
  end
31
51
  end
32
52
  end
@@ -1,3 +1,3 @@
1
1
  module Transcriber
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/transcriber.rb CHANGED
@@ -1,6 +1,42 @@
1
1
  require 'date'
2
- require 'active_support/core_ext/string/inflections'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext/class/attribute_accessors'
4
+ require 'active_support/core_ext/object/try'
5
+ require 'active_support/core_ext/array/wrap'
6
+ require 'active_support/core_ext/hash/slice'
7
+
8
+ require 'transcriber/resource/input_path'
9
+ require 'transcriber/resource/parser'
10
+ require 'transcriber/resource/response'
11
+ require 'transcriber/resource/serialization/boolean'
12
+ require 'transcriber/resource/serialization/date'
13
+ require 'transcriber/resource/serialization/float'
14
+ require 'transcriber/resource/serialization/integer'
15
+ require 'transcriber/resource/serialization/string'
16
+ require 'transcriber/resource/key'
17
+ require 'transcriber/resource/parser/property'
18
+ require 'transcriber/resource/parser/embeddable'
19
+ require 'transcriber/resource/response/property'
20
+ require 'transcriber/resource/response/embeddable'
21
+ require 'transcriber/resource/response/relation'
22
+ require 'transcriber/resource/key/property'
23
+ require 'transcriber/resource/key/association'
24
+ require 'transcriber/resource/key/relation'
25
+ require 'transcriber/resource/key/embeddable'
26
+ require 'transcriber/resource/builder/keys'
27
+ require 'transcriber/resource/builder/relations'
28
+ require 'transcriber/resource/builder/embeddables'
29
+ require 'transcriber/resource/builder/properties'
30
+ require 'transcriber/resource/builder'
31
+ require 'transcriber/resource'
32
+ require 'transcriber/configuration'
3
33
 
4
34
  module Transcriber
5
- autoload :Resource, 'transcriber/resource'
35
+ def self.configuration
36
+ @configuration ||= Transcriber::Configuration.new
37
+ end
38
+
39
+ def self.configure
40
+ yield configuration if block_given?
41
+ end
6
42
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe "has many" do
4
+ context_from_example "has_many/simple" do
5
+ it "sets root" do
6
+ root.resource.should be_a(Hash)
7
+ end
8
+ end
9
+ end
File without changes
data/spec/spec_helper.rb CHANGED
@@ -1 +1,22 @@
1
- require 'transcriber'
1
+ $:.push '../lib'
2
+
3
+ require 'transcriber'
4
+ require 'ostruct'
5
+ require 'json'
6
+ require 'stringio'
7
+ require 'active_support/core_ext/hash/indifferent_access'
8
+
9
+ require 'support/examples'
10
+ require 'support/out'
11
+
12
+ RSpec.configure do |config|
13
+ config.before(:each) do
14
+ [:Example, :ExampleChild].each do |klass|
15
+ Object.send(:remove_const, klass) if Object.const_defined? klass
16
+ end
17
+ end
18
+ end
19
+
20
+ include Transcriber
21
+ include Output
22
+ include Examples
@@ -0,0 +1,21 @@
1
+ module Examples
2
+ def context_from_example(file, &block)
3
+ context "example #{file}" do
4
+ silently {eval File.read("examples/#{file}.rb")}
5
+
6
+ variables = instance_variables.reject {|var| var.to_s =~ /metadata$/}
7
+
8
+ values = variables.inject({}) do |buffer, name|
9
+ method_name = name.to_s.gsub(/@/, "")
10
+ let(method_name) {instance_variable_get(name)}
11
+ buffer.merge method_name => instance_variable_get(name)
12
+ end
13
+
14
+ before :all do
15
+ values.each {|name, value| instance_variable_set "@#{name}", value}
16
+ end
17
+
18
+ instance_eval &block if block
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Output
2
+ def out
3
+ output = StringIO.new
4
+ begin
5
+ $stdout = output
6
+ yield
7
+ ensure
8
+ $stdout = STDOUT
9
+ end
10
+ output.string
11
+ end
12
+
13
+ def silently
14
+ out {yield}
15
+ nil
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Configuration do
4
+ after(:all) do
5
+ Transcriber.configuration.convert_input_keys = nil
6
+ end
7
+
8
+ context "when no custom input keys converter is set" do
9
+ it "returns default key converter" do
10
+ default_handler = Transcriber::Configuration::DefaultHandler
11
+ Transcriber.configuration.convert_input_keys.should == default_handler
12
+ end
13
+ end
14
+
15
+ context "when a custom input keys converter is set" do
16
+ let(:custom_convert_input_keys) {lambda {|keys| keys}}
17
+
18
+ before {Transcriber.configuration.convert_input_keys = custom_convert_input_keys}
19
+
20
+ it "returns custom key converter" do
21
+ Transcriber.configuration.convert_input_keys.should == custom_convert_input_keys
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ module Upcasing
4
+ def self.call(keys)
5
+ keys.map {|key| key.to_s.upcase}
6
+ end
7
+ end
8
+
9
+ describe Resource::InputPath do
10
+ shared_examples_for "an input path" do
11
+ it "brings the correct input path" do
12
+ subject.input_path.should == path
13
+ end
14
+ end
15
+
16
+ context "when a custom path is not defined" do
17
+ context "and there is'nt a custom keys input converter" do
18
+ subject {Resource::Key.new(:name)}
19
+ let(:path) {['name']}
20
+ it_behaves_like "an input path"
21
+ end
22
+
23
+ context "and there is a custom keys input converter" do
24
+ subject {Resource::Key.new(:name, convert_input_keys: Upcasing)}
25
+ let(:path) {['NAME']}
26
+ it_behaves_like "an input path"
27
+ end
28
+ end
29
+
30
+ context "when a custom path is defined" do
31
+ context "and it is a simple name" do
32
+ subject {Resource::Key.new(:name, field: :other_name)}
33
+ let(:path) {['other_name']}
34
+ it_behaves_like "an input path"
35
+ end
36
+
37
+ context "and it is a set of names composed by dot" do
38
+ subject {Resource::Key.new(:name, field: 'a.long.way.to.go')}
39
+ let(:path) {['a', 'long', 'way', 'to', 'go']}
40
+ it_behaves_like "an input path"
41
+ end
42
+
43
+ context "and it is a set of names composed by dot" do
44
+ subject {Resource::Key.new(:name, field: 'a.long.long.way.to.go')}
45
+ let(:path) {['a', 'long', 'long', 'way', 'to', 'go']}
46
+ it_behaves_like "an input path"
47
+ end
48
+
49
+ context "and it is a set of names composed by dot using :start_key instead of :field" do
50
+ subject {Resource::Key.new(:name, start_key: 'a.long.way.to.go')}
51
+ let(:path) {['a', 'long', 'way', 'to', 'go']}
52
+ it_behaves_like "an input path"
53
+ end
54
+
55
+ context "and there is a custom keys input converter" do
56
+ let(:options) do
57
+ {
58
+ start_key: 'yay.upcase.path',
59
+ convert_input_keys: Upcasing
60
+ }
61
+ end
62
+
63
+ subject {Resource::Key.new(:name, options)}
64
+ let(:path) {['YAY', 'UPCASE', 'PATH']}
65
+ it_behaves_like "an input path"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Resource::Key do
4
+ let(:name) {"key_name"}
5
+ let(:options) {{any_option: "here"}}
6
+
7
+ subject {Resource::Property.new(name, options)}
8
+
9
+ describe "#initialize" do
10
+ it "sets key name" do
11
+ subject.name.should == name
12
+ end
13
+
14
+ it "sets options" do
15
+ subject.options.should == options
16
+ end
17
+ end
18
+
19
+ describe "present?" do
20
+ context "when :if option was defined with a proc" do
21
+ context "and it evaluates to false" do
22
+ subject {Resource::Property.new(name, {if: proc {false}})}
23
+
24
+ it "returns false" do
25
+ subject.present?(nil).should be_false
26
+ end
27
+ end
28
+
29
+ context "and it evaluates to true" do
30
+ subject {Resource::Property.new(name, {if: proc {true}})}
31
+
32
+ it "returns true" do
33
+ subject.present?(nil).should be_true
34
+ end
35
+ end
36
+
37
+ context "and it depends on resource instance" do
38
+ subject {Resource::Property.new(name, {if: proc {show}})}
39
+
40
+ it "uses it" do
41
+ resource = OpenStruct.new
42
+ resource.show = true
43
+ subject.present?(resource).should be_true
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end