transcriber 0.0.1 → 0.0.2

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