angular_sprinkles 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,55 @@
1
+ require 'action_view/helpers'
2
+ require 'angular_sprinkles/mixins/js_transformable'
3
+ require 'angular_sprinkles/mixins/initializable'
4
+ require 'angular_sprinkles/data/bind'
5
+
6
+ module AngularSprinkles
7
+ module Helpers
8
+ module BindHelper
9
+ include ::ActionView::Helpers
10
+ include Mixins::JsTransformable
11
+ include Mixins::Initializable
12
+
13
+ def bind(*input)
14
+ input = input.flatten.compact
15
+
16
+ raise ArgumentError if input.empty?
17
+
18
+ yield_to_sprinkles(AngularSprinkles::CONSTRUCTOR_DEFINITION) unless app_initialized?
19
+
20
+ build_chain(input).
21
+ select(&method(:is_uninitialized?)).
22
+ map(&method(:convert_to_empty_js_object_string)).
23
+ tap(&method(:yield_if_any))
24
+
25
+ AngularSprinkles::Data::Bind.new(*input)
26
+ end
27
+
28
+ private
29
+
30
+ def build_chain(input)
31
+ # [:a, :b, :c, :d] => [[:a], [:a, :b], [:a, :b, :c]]
32
+ (1...input.count).inject([]) { |acc,i| acc << input.first(i) }
33
+ end
34
+
35
+ def is_uninitialized?(var)
36
+ !var_initialized?(var)
37
+ end
38
+
39
+ def convert_to_empty_js_object_string(var)
40
+ set_prototype_variable(var.join('.'), {})
41
+ end
42
+
43
+ def yield_if_any(chain)
44
+ yield_to_sprinkles(chain.join(";\n") + ';') if chain.any?
45
+ end
46
+
47
+ def yield_to_sprinkles(content)
48
+ content_for(:sprinkles) do
49
+ content_tag(:script, content.html_safe)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ require 'action_view/helpers'
2
+
3
+ module AngularSprinkles
4
+ module Helpers
5
+ module DirectiveHelper
6
+ include ::ActionView::Helpers
7
+
8
+ def directive(name, input = {}, options = {}, &block)
9
+ raise TypeError unless [String, Symbol, Array].include?(name.class)
10
+ raise TypeError unless input.is_a?(Hash)
11
+ raise TypeError unless options.is_a?(Hash)
12
+
13
+ input.symbolize_keys!
14
+ options.symbolize_keys!
15
+
16
+ tag_contents = block_given? ? capture(&block) : ''
17
+ tag_attributes = {}
18
+ tag_name = options[:tag] || :div
19
+
20
+ directive_attributes = Hash[input.map { |k, v| [k, v.to_json] }]
21
+ name_attributes = Hash[[name].flatten.product([''])]
22
+
23
+ tag_attributes[:data] = [directive_attributes, name_attributes].compact.inject(&:merge)
24
+ tag_attributes.merge!(options.slice(:class, :style))
25
+
26
+ content_tag(tag_name, tag_contents, tag_attributes)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ require 'angular_sprinkles/helpers/bind_helper'
2
+ require 'angular_sprinkles/helpers/directive_helper'
3
+
4
+ module AngularSprinkles
5
+ module Helpers
6
+ include Helpers::BindHelper
7
+ include Helpers::DirectiveHelper
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module AngularSprinkles
2
+ module Mixins
3
+ module Initializable
4
+ def app_initialized?
5
+ @_sprinkles_app_initialized or (@_sprinkles_app_initialized = true) && false
6
+ end
7
+
8
+ def var_initialized?(variable)
9
+ variable = [variable].flatten
10
+ initialized_variables[variable] or (initialized_variables[variable] = true) && false
11
+ end
12
+
13
+ private
14
+
15
+ def initialized_variables
16
+ @_sprinkles_initialized_variables ||= {}
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module AngularSprinkles
2
+ module Mixins
3
+ module JsTransformable
4
+ def to_ctrl_variable(str)
5
+ "#{AngularSprinkles::CONTROLLER_NAME}.#{str}"
6
+ end
7
+
8
+ def set_prototype_variable(key, value)
9
+ or_equals_js(to_ctrl_prototype_variable(key), value)
10
+ end
11
+
12
+ private
13
+
14
+ def or_equals_js(key, value)
15
+ "#{key} = #{key} || #{value.to_json}";
16
+ end
17
+
18
+ def to_ctrl_prototype_variable(str)
19
+ "#{AngularSprinkles::CONTROLLER_FN}.prototype.#{str}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+
3
+ module AngularSprinkles
4
+ class Railtie < ::Rails::Railtie
5
+ initializer "angular_sprinkles.setup_view_helpers" do
6
+ ActiveSupport.on_load(:action_view) do
7
+ include ::AngularSprinkles::Helpers
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module AngularSprinkles
2
+ PREFIX = 'sprinkles'
3
+ APP_NAME = "#{PREFIX}App"
4
+ CONTROLLER_NAME = "#{PREFIX}Ctrl"
5
+ CONTROLLER_FN = "window.#{CONTROLLER_NAME}Fn"
6
+ CONSTRUCTOR_DEFINITION = "#{CONTROLLER_FN} = #{CONTROLLER_FN} || function(){}"
7
+ end
8
+
9
+ require 'angular_sprinkles/helpers'
10
+ require 'angular_sprinkles/decorators'
11
+ require 'angular_sprinkles/controller'
12
+ require 'angular_sprinkles/engine'
13
+ require 'angular_sprinkles/railtie'
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Controller do
4
+ class StubController
5
+ include AngularSprinkles::Controller
6
+
7
+ def initialize(params)
8
+ @params = params
9
+ end
10
+
11
+ def index
12
+ assignable(@params)
13
+ end
14
+ end
15
+
16
+ let(:key) { 'key' }
17
+ let(:value) { 'value' }
18
+ let(:controller) { StubController.new(params) }
19
+
20
+ context 'when given a hash' do
21
+ context 'and it is not empty' do
22
+ let(:params) { { key => value } }
23
+
24
+ it 'preloads the input data' do
25
+ result = [
26
+ AngularSprinkles::CONSTRUCTOR_DEFINITION,
27
+ %{#{AngularSprinkles::CONTROLLER_FN}.prototype.#{key} = #{AngularSprinkles::CONTROLLER_FN}.prototype.#{key} || "#{value}"}
28
+ ]
29
+
30
+ expect(controller.index).to eq(result)
31
+ end
32
+ end
33
+
34
+ context 'and it is an empty hash' do
35
+ let(:params) { {} }
36
+
37
+ it 'only returns the prototype definition' do
38
+ result = [AngularSprinkles::CONSTRUCTOR_DEFINITION]
39
+ expect(controller.index).to eq(result)
40
+ end
41
+ end
42
+ end
43
+
44
+ context 'when given anything else' do
45
+ let(:params) { value }
46
+
47
+ it 'raises an exception' do
48
+ expect { controller.index }.to raise_error(TypeError)
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Data::Bind do
4
+ let(:keys) { [:brewhouse, :software] }
5
+ let(:object) { AngularSprinkles::Data::Bind.new(*keys) }
6
+
7
+ it 'returns the concatenation of the two keys' do
8
+ expect(object.to_json).to eq("#{AngularSprinkles::CONTROLLER_NAME}.#{keys.join('.')}")
9
+ expect(object.to_s).to eq("#{AngularSprinkles::CONTROLLER_NAME}.#{keys.join('.')}")
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Decorators::Bind do
4
+ class StubObject
5
+ attr_reader :some_attr
6
+
7
+ def initialize(some_attr)
8
+ @some_attr = some_attr
9
+ end
10
+ end
11
+
12
+ class StubContext
13
+ def bind(*args)
14
+ end
15
+
16
+ def var_initialized?(*args)
17
+ end
18
+
19
+ def set_prototype_variable(*args)
20
+ end
21
+
22
+ def content_for(*args)
23
+ end
24
+ end
25
+
26
+ let(:context) { StubContext.new }
27
+ let(:context_proc) { ->{ context } }
28
+
29
+ let(:attr) { :brewhouse }
30
+ let(:key) { 'software' }
31
+ let(:object) { StubObject.new(attr) }
32
+
33
+ subject { AngularSprinkles::Decorators::Bind.new(object, key, context_proc) }
34
+
35
+ describe '#bind' do
36
+ context 'when no arguments are passed' do
37
+ it 'creates the necessary object binding' do
38
+ expect(context).to receive(:bind)
39
+ subject.bind
40
+ end
41
+ end
42
+
43
+ context 'when more than one argument is passed' do
44
+ it 'creates the necessary object binding' do
45
+ expect(context).to receive(:bind).with(key, nil)
46
+ subject.bind
47
+ end
48
+
49
+ it 'creates additional bindings' do
50
+ expect(context).to receive(:bind).with(key, :some_attr)
51
+ subject.bind(:some_attr)
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Helpers::BindHelper do
4
+ class StubClass
5
+ include AngularSprinkles::Helpers::BindHelper
6
+ end
7
+
8
+ let(:stub) { StubClass.new }
9
+
10
+ before { allow(stub).to receive(:content_for).and_return(true) }
11
+
12
+ context 'when 0 arguments' do
13
+ it 'raises an argument error' do
14
+ expect { stub.bind }.to raise_error(ArgumentError)
15
+ end
16
+ end
17
+
18
+ context 'when 1 argument' do
19
+ let(:var) { :brewhouse }
20
+
21
+ it 'returns the variable name given as a js string' do
22
+ expect(stub.bind(var).to_json).
23
+ to(eq("#{AngularSprinkles::CONTROLLER_NAME}.#{var}"))
24
+ end
25
+
26
+ it 'only yields the constructor definition to the view' do
27
+ expect(stub).to receive(:yield_to_sprinkles).with(AngularSprinkles::CONSTRUCTOR_DEFINITION)
28
+ stub.bind(var)
29
+ end
30
+ end
31
+
32
+ context 'when 2 arguments' do
33
+ let(:vars) { [:brewhouse, :software] }
34
+
35
+ it 'returns the variables chained together as a js string' do
36
+ expect(stub.bind(*vars).to_json).
37
+ to(eq("#{AngularSprinkles::CONTROLLER_NAME}.#{vars.join('.')}"))
38
+ end
39
+
40
+ it 'yields the contructor definition and the first variable to the function to prototype' do
41
+ expect(stub).to receive(:yield_to_sprinkles).with(AngularSprinkles::CONSTRUCTOR_DEFINITION)
42
+ expect(stub).to receive(:yield_to_sprinkles).
43
+ with("#{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first} = #{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first} || {};")
44
+
45
+ stub.bind(*vars)
46
+ end
47
+ end
48
+
49
+ context 'when 3 arguments' do
50
+ let(:vars) { [:brewhouse, :software, :is_neat] }
51
+
52
+ it 'returns the variables chained together as a js string' do
53
+ expect(stub.bind(*vars).to_json).
54
+ to(eq("#{AngularSprinkles::CONTROLLER_NAME}.#{vars.join('.')}"))
55
+ end
56
+
57
+ it 'yields the constructor definition and the first variable to the function to prototype' do
58
+ expect(stub).to receive(:yield_to_sprinkles).with(AngularSprinkles::CONSTRUCTOR_DEFINITION)
59
+ expect(stub).to receive(:yield_to_sprinkles).
60
+ with(%{#{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first} = #{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first} || {};
61
+ #{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first(2).join('.')} = #{AngularSprinkles::CONTROLLER_FN}.prototype.#{vars.first(2).join('.')} || {};})
62
+
63
+ stub.bind(*vars)
64
+ end
65
+ end
66
+
67
+ context 'when bind is called more than once' do
68
+ let(:vars) { [:brewhouse, :software, :is_neat] }
69
+
70
+ context 'and it is with the same variables' do
71
+ it 'yields the constructor definition and the first variable to the function to prototype' do
72
+ expect(stub).to receive(:content_for).twice
73
+ 5.times { stub.bind(*vars) }
74
+ end
75
+ end
76
+
77
+ context 'and it is called with different sets of variables' do
78
+ it 'yields the constructor definition, first variable, and a chain of the first two variables' do
79
+ expect(stub).to receive(:content_for).exactly(3).times
80
+
81
+ stub.bind(vars.first) # yields the constructor
82
+ stub.bind(vars.first) # yields nothing
83
+ stub.bind(*vars.first(2)) # yields declaration for :brewhouse
84
+ stub.bind(*vars) # yields declaration for [:brewhouse, :software]
85
+ stub.bind(*vars) # yields nothing
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'when the constructor has already been yielded' do
91
+ let(:var) { :brewhouse }
92
+
93
+ before { allow(stub).to receive(:app_initialized?).and_return(true) }
94
+
95
+ it 'does not yield anything with only one argument' do
96
+ expect(stub).not_to receive(:content_for)
97
+ stub.bind(var)
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Helpers::DirectiveHelper do
4
+ class StubClass
5
+ include AngularSprinkles::Helpers::DirectiveHelper
6
+ end
7
+
8
+ let(:stub) { StubClass.new }
9
+ let(:var) { 'brewhouse' }
10
+ let(:var2) { 'software' }
11
+ let(:var_binded) { AngularSprinkles::Data::Bind.new(var) }
12
+ let(:var2_binded) { AngularSprinkles::Data::Bind.new(var2) }
13
+
14
+ it 'renders a tag' do
15
+ html = stub.directive(:blink)
16
+
17
+ expect(html).to eq('<div data-blink=""></div>')
18
+ end
19
+
20
+ it 'renders a tag for multiple directives' do
21
+ html = stub.directive([:blink, :block])
22
+
23
+ expect(html).to eq('<div data-blink="" data-block=""></div>')
24
+ end
25
+
26
+ it 'renders a tag with initial values' do
27
+ html = stub.directive(:blink, { input: 42 })
28
+
29
+ expect(html).to eq('<div data-blink="" data-input="42"></div>')
30
+ end
31
+
32
+ it 'renders a tag with a binding as the initial value' do
33
+ html = stub.directive(:blink, { input: var_binded, some_other_input: [1,2,3] })
34
+
35
+ expect(html).
36
+ to eq(%{<div data-blink="" data-input="#{AngularSprinkles::CONTROLLER_NAME}.#{var}" data-some-other-input="[1,2,3]"></div>})
37
+ end
38
+
39
+ it 'renders a tag with a nested binding as the initial value' do
40
+ html = stub.directive(:blink, { input: var2_binded })
41
+
42
+ expect(html).
43
+ to eq(%{<div data-blink="" data-input="#{AngularSprinkles::CONTROLLER_NAME}.#{var2}"></div>})
44
+ end
45
+
46
+ it 'renders a tag with class and style' do
47
+ klass = 'brewhouse'
48
+ style = 'color:red;'
49
+
50
+ html = stub.directive(:blink, {}, { class: klass, style: style })
51
+
52
+ expect(html).to eq(%{<div class="#{klass}" data-blink="" style="#{style}"></div>})
53
+ end
54
+
55
+ it 'raises an exception if the name is not a string, symbol, or array' do
56
+ expect { stub.directive(Object.new) }.to raise_error(TypeError)
57
+ expect { stub.directive(1) }.to raise_error(TypeError)
58
+ expect { stub.directive({}) }.to raise_error(TypeError)
59
+ end
60
+
61
+ it 'raises an exception if the input and options are not hashes' do
62
+ expect { stub.directive(:blink, 1) }.to raise_error(TypeError)
63
+ expect { stub.directive(:blink, {}, :brewhouse) }.to raise_error(TypeError)
64
+ end
65
+
66
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Mixins::Initializable do
4
+ class StubClass
5
+ include AngularSprinkles::Mixins::Initializable
6
+ end
7
+
8
+ let(:stub_class) { StubClass.new }
9
+
10
+ describe '#app_initialized?' do
11
+ it 'returns false the first time and true after that' do
12
+ expect(stub_class.app_initialized?).to eq(false)
13
+ expect(stub_class.app_initialized?).to eq(true)
14
+ expect(stub_class.app_initialized?).to eq(true)
15
+ end
16
+ end
17
+
18
+ describe '#var_initialized?' do
19
+ it 'returns false the first time and true after that' do
20
+ var = 'a.b'
21
+
22
+ expect(stub_class.var_initialized?(var)).to eq(false)
23
+ expect(stub_class.var_initialized?(var)).to eq(true)
24
+ expect(stub_class.var_initialized?(var)).to eq(true)
25
+
26
+ expect(stub_class.var_initialized?(var + '.c')).to eq(false)
27
+ expect(stub_class.var_initialized?(var + '.c')).to eq(true)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe AngularSprinkles::Mixins::JsTransformable do
4
+ class StubClass
5
+ include AngularSprinkles::Mixins::JsTransformable
6
+ end
7
+
8
+ let(:stub_class) { StubClass.new }
9
+
10
+ describe "#to_ctrl_variable" do
11
+ it 'returns a javascript variable string' do
12
+ str = 'brewhouse'
13
+
14
+ expect(stub_class.to_ctrl_variable(str)).
15
+ to(eq("#{AngularSprinkles::CONTROLLER_NAME}.#{str}"))
16
+ end
17
+ end
18
+
19
+ describe "#set_prototype_variable" do
20
+ it 'returns a javascript variable string with a preset value' do
21
+ key, value = 'brewhouse', 'software'
22
+
23
+ expect(stub_class.set_prototype_variable(key, value)).
24
+ to(eq("#{AngularSprinkles::CONTROLLER_FN}.prototype.#{key} = #{AngularSprinkles::CONTROLLER_FN}.prototype.#{key} || #{value.to_json}"))
25
+ end
26
+ end
27
+
28
+ # these methods moved to private
29
+
30
+ # describe "#to_ctrl_prototype_variable" do
31
+ # it 'returns a javascript prototype variable string' do
32
+ # str = 'brewhouse'
33
+
34
+ # expect(stub_class.to_ctrl_prototype_variable(str)).
35
+ # to(eq("#{AngularSprinkles::CONTROLLER_FN}.prototype.#{str}"))
36
+ # end
37
+ # end
38
+
39
+ # describe "#or_equals_js" do
40
+ # it 'returns a javascript variable string' do
41
+ # expect(stub_class.or_equals_js('brewhouse', 'bacon')).
42
+ # to(eq('brewhouse = brewhouse || "bacon"'))
43
+ # end
44
+ # end
45
+ end
@@ -0,0 +1,30 @@
1
+ require 'simplecov'
2
+
3
+ module SimpleCov::Configuration
4
+ def clean_filters
5
+ @filters = []
6
+ end
7
+ end
8
+
9
+ SimpleCov.configure do
10
+ clean_filters
11
+ load_profile 'test_frameworks'
12
+ end
13
+
14
+ ENV["COVERAGE"] && SimpleCov.start do
15
+ add_filter "/.rbenv/"
16
+ end
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
+
20
+ require 'rspec'
21
+ require 'rails'
22
+ require 'angular_sprinkles'
23
+
24
+ # Requires supporting files with custom matchers and macros, etc,
25
+ # in ./support/ and its subdirectories.
26
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
27
+
28
+ RSpec.configure do |config|
29
+
30
+ end