halogen 0.0.1

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.
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ module Embeds
5
+ class Definition < Halogen::Definition # :nodoc:
6
+ # @return [true] if nothing is raised
7
+ #
8
+ # @raise [Halogen::InvalidDefinition] if the definition is invalid
9
+ #
10
+ def validate
11
+ super
12
+
13
+ return true if procedure
14
+
15
+ fail InvalidDefinition, "Embed #{name} must be defined with a proc"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ class InvalidCollection < StandardError; end # :nodoc
5
+ class InvalidDefinition < StandardError; end # :nodoc
6
+ class InvalidResource < StandardError; end # :nodoc
7
+ end
@@ -0,0 +1,37 @@
1
+ module Halogen
2
+ module HashUtil # :nodoc:
3
+ extend self
4
+
5
+ # Transform hash keys into strings if necessary
6
+ #
7
+ # @param hash [Hash]
8
+ #
9
+ # @return [Hash]
10
+ #
11
+ def stringify_keys!(hash)
12
+ transform_keys!(hash, &:to_s)
13
+ end
14
+
15
+ # Transform hash keys into symbols if necessary
16
+ #
17
+ # @param hash [Hash]
18
+ #
19
+ # @return [Hash]
20
+ #
21
+ def symbolize_keys!(hash)
22
+ transform_keys!(hash, &:to_sym)
23
+ end
24
+
25
+ # Transform hash keys according to block
26
+ #
27
+ # @param hash [Hash]
28
+ #
29
+ # @return [Hash]
30
+ #
31
+ def transform_keys!(hash)
32
+ hash.keys.each { |key| hash[yield(key)] = hash.delete(key) }
33
+
34
+ hash
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ module Links # :nodoc:
5
+ def self.included(base) # :nodoc:
6
+ base.extend ClassMethods
7
+
8
+ base.send :include, InstanceMethods
9
+ end
10
+
11
+ module ClassMethods # :nodoc:
12
+ # @return [Halogen::Embeds::Definition]
13
+ #
14
+ def link(name, *args, &procedure)
15
+ definitions.add(Definition.new(name, *args, procedure))
16
+ end
17
+ end
18
+
19
+ module InstanceMethods # :nodoc:
20
+ # @return [Hash] the rendered hash with links, if any
21
+ #
22
+ def render
23
+ decorate_render :links, super
24
+ end
25
+
26
+ # @return [Hash] links from definitions
27
+ #
28
+ def links
29
+ render_definitions(Definition.name) do |definition, result|
30
+ attrs = definition.options.fetch(:attrs, {})
31
+
32
+ href = definition.value(self)
33
+
34
+ result[definition.name] = attrs.merge(href: href) if href
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ require 'halogen/links/definition'
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ module Links
5
+ class Definition < Halogen::Definition # :nodoc
6
+ # Links have special keywords that other definitions don't, so override
7
+ # the standard initializer to build options from keywords
8
+ #
9
+ def initialize(name, *args, procedure)
10
+ super name, self.class.build_options(args), procedure
11
+ end
12
+
13
+ # @return [true] if nothing is raised
14
+ #
15
+ # @raise [Halogen::InvalidDefinition] if the definition is invalid
16
+ #
17
+ def validate
18
+ super
19
+
20
+ return true if procedure || options.key?(:value)
21
+
22
+ fail InvalidDefinition,
23
+ 'Link requires either procedure or explicit value'
24
+ end
25
+
26
+ class << self
27
+ # Build hash of options from flexible definition arguments
28
+ #
29
+ # @param args [Array] the raw definition arguments
30
+ #
31
+ # @return [Hash] standardized hash of options
32
+ #
33
+ def build_options(args)
34
+ {}.tap do |options|
35
+ options.merge!(args.pop) if args.last.is_a?(Hash)
36
+
37
+ options[:attrs] ||= {}
38
+ options[:attrs].merge!(build_attrs(args))
39
+ end
40
+ end
41
+
42
+ # @param keywords [Array] array of special keywords
43
+ #
44
+ # @raise [Halogen::InvalidDefinition] if a keyword is unrecognized
45
+ #
46
+ def build_attrs(keywords)
47
+ keywords.each_with_object({}) do |keyword, attrs|
48
+ case keyword
49
+ when :templated, 'templated'
50
+ attrs[:templated] = true
51
+ else
52
+ fail InvalidDefinition, "Unrecognized link keyword: #{keyword}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ module Properties # :nodoc:
5
+ def self.included(base) # :nodoc:
6
+ base.extend ClassMethods
7
+
8
+ base.send :include, InstanceMethods
9
+ end
10
+
11
+ module ClassMethods # :nodoc:
12
+ # @param name [Symbol, String]
13
+ # @param options [nil, Hash]
14
+ #
15
+ # @return [Halogen::Embeds::Definition]
16
+ #
17
+ def property(name, options = {}, &procedure)
18
+ definitions.add(Definition.new(name, options, procedure))
19
+ end
20
+ end
21
+
22
+ module InstanceMethods # :nodoc:
23
+ # @return [Hash] the rendered hash with properties, if any
24
+ #
25
+ def render
26
+ super.merge(properties)
27
+ end
28
+
29
+ # @return [Hash] properties from definitions
30
+ #
31
+ def properties
32
+ render_definitions(Definition.name) do |definition, result|
33
+ result[definition.name] = definition.value(self)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ require 'halogen/properties/definition'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ module Properties
5
+ class Definition < Halogen::Definition # :nodoc
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ # Provide Rails-specific extensions if loaded in a Rails application
5
+ #
6
+ class Railtie < ::Rails::Railtie
7
+ initializer 'halogen' do |_app|
8
+ Halogen.config.extensions << ::Rails.application.routes.url_helpers
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ # Behavior for representers with a single primary resource
5
+ #
6
+ module Resource
7
+ def self.included(base) # :nodoc:
8
+ if base.included_modules.include?(Collection)
9
+ fail InvalidResource, "#{base.name} has already defined a collection"
10
+ end
11
+
12
+ base.extend ClassMethods
13
+
14
+ base.send :include, InstanceMethods
15
+
16
+ base.send :attr_reader, :resource
17
+
18
+ base.class.send :attr_accessor, :resource_name
19
+ end
20
+
21
+ module ClassMethods # :nodoc:
22
+ # @param name [Symbol, String] name of the resource
23
+ #
24
+ # @return [Module] self
25
+ #
26
+ def define_resource(name)
27
+ self.resource_name = name.to_s
28
+
29
+ alias_method name, :resource
30
+ end
31
+
32
+ # Override standard property definition for resource-based representers
33
+ #
34
+ # @param name [Symbol, String] name of the property
35
+ # @param options [nil, Hash] property options for definition
36
+ #
37
+ def property(name, options = {}, &procedure)
38
+ super.tap do |definition|
39
+ unless definition.procedure || definition.options.key?(:value)
40
+ definition.procedure = proc { resource.send(name) }
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ module InstanceMethods # :nodoc:
47
+ # Override standard initializer to assign primary resource
48
+ #
49
+ # @param resource [Object] the primary resource
50
+ #
51
+ def initialize(resource, *args)
52
+ @resource = resource
53
+
54
+ super *args
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Halogen
4
+ VERSION = '0.0.1' # :nodoc:
5
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Halogen::Collection do
6
+ let :klass do
7
+ Class.new do
8
+ include Halogen
9
+ include Halogen::Collection
10
+ end
11
+ end
12
+
13
+ describe '.included' do
14
+ it 'raises error if base is already a resource' do
15
+ resource_class = Class.new do
16
+ include Halogen
17
+ include Halogen::Resource
18
+ end
19
+
20
+ expect {
21
+ resource_class.send :include, Halogen::Collection
22
+ }.to raise_error do |exception|
23
+ expect(exception).to be_an_instance_of(Halogen::InvalidCollection)
24
+ expect(exception.message).to match(/has already defined a resource/i)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe Halogen::Collection::ClassMethods do
30
+ describe '#define_collection' do
31
+ it 'handles string argument' do
32
+ klass.define_collection 'goats'
33
+
34
+ expect(klass.collection_name).to eq('goats')
35
+ end
36
+
37
+ it 'handles symbol argument' do
38
+ klass.define_collection :goats
39
+
40
+ expect(klass.collection_name).to eq('goats')
41
+ end
42
+ end
43
+ end
44
+
45
+ describe Halogen::Collection::InstanceMethods do
46
+ describe '#embed?' do
47
+ it 'returns true if super is true' do
48
+ klass.collection_name = 'bar'
49
+
50
+ klass.embed(:foo) { klass.new }
51
+
52
+ repr = klass.new(embed: { foo: 1 })
53
+
54
+ expect(repr.embed?('foo')).to eq(true)
55
+ end
56
+
57
+ it 'returns true if key matches collection name' do
58
+ klass.collection_name = 'foo'
59
+
60
+ klass.embed(:foo) { klass.new }
61
+
62
+ repr = klass.new(embed: { foo: 0 })
63
+
64
+ expect(repr.embed?('foo')).to eq(true)
65
+ end
66
+
67
+ it 'returns false if no conditions match' do
68
+ klass.collection_name = 'bar'
69
+
70
+ klass.embed(:foo) { klass.new }
71
+
72
+ repr = klass.new(embed: { foo: 0 })
73
+
74
+ expect(repr.embed?('foo')).to eq(false)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Halogen::Configuration do
6
+ describe '#extensions' do
7
+ it 'is empty array by default' do
8
+ expect(Halogen::Configuration.new.extensions).to eq([])
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Halogen::Definition do
6
+ describe '#initialize' do
7
+ it 'symbolizes option keys' do
8
+ definition = Halogen::Definition.new(
9
+ :name, { 'value' => 'some value', 'foo' => 'bar' }, nil)
10
+
11
+ expect(definition.options.keys).to eq([:value, :foo])
12
+ end
13
+ end
14
+
15
+ describe '#value' do
16
+ it 'returns value from options if present' do
17
+ definition = Halogen::Definition.new(:name, { value: 'some value' }, nil)
18
+
19
+ expect(definition.value(nil)).to eq('some value')
20
+ end
21
+
22
+ it 'evaluates procedure if value from options is missing' do
23
+ definition = Halogen::Definition.new(:name, {}, proc { size })
24
+
25
+ expect(definition.value('foo')).to eq(3)
26
+ end
27
+ end
28
+
29
+ describe '#enabled?' do
30
+ it 'is true if definition is not guarded' do
31
+ definition = Halogen::Definition.new(:name, {}, nil)
32
+
33
+ expect(definition.enabled?(nil)).to eq(true)
34
+ end
35
+
36
+ describe 'when guard is a proc' do
37
+ let :definition do
38
+ Halogen::Definition.new(:name, { if: proc { empty? } }, nil)
39
+ end
40
+
41
+ it 'is true if condition passes' do
42
+ expect(definition.enabled?('')).to eq(true)
43
+ end
44
+
45
+ it 'is false if condition fails' do
46
+ expect(definition.enabled?('foo')).to eq(false)
47
+ end
48
+ end
49
+
50
+ describe 'when guard is a method name' do
51
+ let :definition do
52
+ Halogen::Definition.new(:name, { if: :empty? }, nil)
53
+ end
54
+
55
+ it 'is true if condition passes' do
56
+ expect(definition.enabled?('')).to eq(true)
57
+ end
58
+
59
+ it 'is false if condition fails' do
60
+ expect(definition.enabled?('foo')).to eq(false)
61
+ end
62
+ end
63
+
64
+ describe 'when guard is truthy' do
65
+ it 'is true if condition passes' do
66
+ definition = Halogen::Definition.new(:name, { if: true }, nil)
67
+
68
+ expect(definition.enabled?(nil)).to eq(true)
69
+ end
70
+
71
+ it 'is false if condition fails' do
72
+ definition = Halogen::Definition.new(:name, { if: false }, nil)
73
+
74
+ expect(definition.enabled?(nil)).to eq(false)
75
+ end
76
+ end
77
+
78
+ describe 'when guard is negated' do
79
+ let :definition do
80
+ Halogen::Definition.new(:name, { unless: proc { empty? } }, nil)
81
+ end
82
+
83
+ it 'is false if condition passes' do
84
+ expect(definition.enabled?('')).to eq(false)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#validate' do
90
+ it 'returns true for valid definition' do
91
+ definition = Halogen::Definition.new(:name, { value: 'value' }, nil)
92
+
93
+ expect(definition.validate).to eq(true)
94
+ end
95
+
96
+ it 'raises error for invalid definition' do
97
+ definition = Halogen::Definition.new(
98
+ :name, { value: 'value' }, proc { 'value' })
99
+
100
+ expect {
101
+ definition.validate
102
+ }.to raise_error(Halogen::InvalidDefinition) do |exception|
103
+ expect(exception.message).to(
104
+ eq('Cannot specify both value and procedure for name'))
105
+ end
106
+ end
107
+ end
108
+ end