instedd-pigeon 0.1.1

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 +25 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +140 -0
  6. data/Rakefile +1 -0
  7. data/app/assets/images/pigeon/android_local_gateway_preview.png +0 -0
  8. data/app/assets/images/pigeon/android_local_gateway_qr.png +0 -0
  9. data/app/assets/images/pigeon/other_local_gateway_preview.png +0 -0
  10. data/app/assets/javascripts/pigeon/qst-server-wizard.js.coffee +58 -0
  11. data/app/assets/javascripts/pigeon/template.js.coffee +43 -0
  12. data/app/assets/javascripts/pigeon/twitter-template.js.coffee +38 -0
  13. data/app/assets/javascripts/pigeon/wizard.js.coffee +95 -0
  14. data/app/assets/javascripts/pigeon.js +2 -0
  15. data/app/assets/stylesheets/pigeon.css.sass +13 -0
  16. data/app/controllers/pigeon/twitter_controller.rb +44 -0
  17. data/app/helpers/.gitkeep +0 -0
  18. data/app/helpers/pigeon/channel_helper.rb +93 -0
  19. data/app/helpers/pigeon/renderer/base.rb +172 -0
  20. data/app/helpers/pigeon/renderer/channel_renderer.rb +58 -0
  21. data/app/helpers/pigeon/renderer.rb +8 -0
  22. data/app/helpers/pigeon/tag_helper.rb +26 -0
  23. data/app/helpers/pigeon/template_helper.rb +20 -0
  24. data/app/helpers/pigeon/text_helper.rb +16 -0
  25. data/app/models/.gitkeep +0 -0
  26. data/app/models/pigeon/channel.rb +220 -0
  27. data/app/models/pigeon/channel_attribute.rb +64 -0
  28. data/app/models/pigeon/channel_schema.rb +113 -0
  29. data/app/models/pigeon/nested_attribute.rb +23 -0
  30. data/app/models/pigeon/nested_scopes.rb +34 -0
  31. data/app/models/pigeon/nuntium_channel.rb +92 -0
  32. data/app/models/pigeon/verboice_channel.rb +82 -0
  33. data/app/views/pigeon/twitter/callback.html.erb +6 -0
  34. data/config/routes.rb +4 -0
  35. data/config/schemas/nuntium/nuntium.yml +210 -0
  36. data/config/schemas/nuntium/qst-server.yml +180 -0
  37. data/config/schemas/nuntium/twitter.yml +56 -0
  38. data/config/schemas/verboice/verboice.yml +76 -0
  39. data/lib/pigeon/engine.rb +30 -0
  40. data/lib/pigeon/errors.rb +28 -0
  41. data/lib/pigeon/initializer.rb +13 -0
  42. data/lib/pigeon/nuntium.rb +10 -0
  43. data/lib/pigeon/verboice.rb +10 -0
  44. data/lib/pigeon/version.rb +3 -0
  45. data/lib/pigeon.rb +25 -0
  46. data/pigeon.gemspec +24 -0
  47. data/spec/data/test_schemas.yml +21 -0
  48. data/spec/dummy/README.rdoc +261 -0
  49. data/spec/dummy/Rakefile +7 -0
  50. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  51. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  52. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  53. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  54. data/spec/dummy/app/mailers/.gitkeep +0 -0
  55. data/spec/dummy/app/models/.gitkeep +0 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/spec/dummy/config/application.rb +59 -0
  58. data/spec/dummy/config/boot.rb +10 -0
  59. data/spec/dummy/config/database.yml +25 -0
  60. data/spec/dummy/config/environment.rb +5 -0
  61. data/spec/dummy/config/environments/development.rb +37 -0
  62. data/spec/dummy/config/environments/production.rb +67 -0
  63. data/spec/dummy/config/environments/test.rb +37 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/inflections.rb +15 -0
  66. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  67. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  68. data/spec/dummy/config/initializers/session_store.rb +8 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/dummy/config/locales/en.yml +5 -0
  71. data/spec/dummy/config/routes.rb +3 -0
  72. data/spec/dummy/config.ru +4 -0
  73. data/spec/dummy/lib/assets/.gitkeep +0 -0
  74. data/spec/dummy/log/.gitkeep +0 -0
  75. data/spec/dummy/public/404.html +26 -0
  76. data/spec/dummy/public/422.html +26 -0
  77. data/spec/dummy/public/500.html +25 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/dummy/script/rails +6 -0
  80. data/spec/helpers/pigeon/channel_helper_spec.rb +173 -0
  81. data/spec/helpers/pigeon/renderer/base_spec.rb +109 -0
  82. data/spec/helpers/pigeon/renderer/channel_renderer_spec.rb +61 -0
  83. data/spec/helpers/pigeon/tag_helper_spec.rb +36 -0
  84. data/spec/helpers/pigeon/template_helper_spec.rb +57 -0
  85. data/spec/models/pigeon/channel_attribute_spec.rb +98 -0
  86. data/spec/models/pigeon/channel_schema_spec.rb +63 -0
  87. data/spec/models/pigeon/channel_spec.rb +205 -0
  88. data/spec/models/pigeon/nuntium_channel_spec.rb +53 -0
  89. data/spec/spec_helper.rb +43 -0
  90. data/spec/support/active_model_lint.rb +15 -0
  91. data/spec/support/test_schemas.rb +12 -0
  92. metadata +254 -0
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ spec/dummy/db/*.sqlite3
20
+ spec/dummy/log/*.log
21
+ spec/dummy/tmp/
22
+ spec/dummy/.sass-cache
23
+ spec/dummy/config/initializers/pigeon.rb
24
+
25
+
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@pigeon --create
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pigeon.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec-rails'
7
+ gem 'sqlite3'
8
+ gem 'rspec-html-matchers', :group => :test
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ary Borenszweig
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # Pigeon
2
+
3
+ This gem handles creating, updating and destroying
4
+ [Nuntium](http://nuntium.instedd.org) and
5
+ [Verboice](http://verboice.instedd.org) channels in your Rails application.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'instedd-pigeon', :require => 'pigeon'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ Create an initializer to configure Pigeon (eg. `config/initializers/pigeon.rb`):
20
+
21
+ Pigeon.setup do |config|
22
+ config.application_name = 'My application'
23
+
24
+ config.nuntium_host = 'http://nuntium.instedd.org'
25
+ config.nuntium_account = 'nuntium_account'
26
+ config.nuntium_app = 'nuntium_application'
27
+ config.nuntium_app_password = 'password'
28
+
29
+ config.verboice_host = 'http://verboice.instedd.org'
30
+ config.verboice_account = 'account@example.com'
31
+ config.verboice_password = 'password'
32
+ config.verboice_default_call_flow = 'Default Call Flow'
33
+
34
+ # If you want to support Nuntium Twitter channels, get your Twitter
35
+ # consumer keys from https://dev.twitter.com/apps
36
+ config.twitter_consumer_key = 'CONSUMER_KEY'
37
+ config.twitter_consumer_secret = 'CONSUMER_SECRET'
38
+ end
39
+
40
+ Add Pigeon assets to your application, for example by adding to
41
+ `app/assets/javascripts/application.js` the line
42
+
43
+ //= require pigeon
44
+
45
+ and to `app/assets/stylesheets/application.css`
46
+
47
+ /*
48
+ *= require pigeon
49
+ */
50
+
51
+ If you need to support Nuntium Twitter channels, mount the Pigeon engine by
52
+ adding to your `routes.rb`
53
+
54
+ mount Pigeon::Engine => '/pigeon'
55
+
56
+ It is **strongly advised** to filter the engine's request through
57
+ authentication. If your application uses Devise, you can easily do it by
58
+ mounting the engine with `authenticate`:
59
+
60
+ authenticate :user do
61
+ mount Pigeon::Engine => '/pigeon'
62
+ end
63
+
64
+ Once properly configured, the gem provides a couple of classes
65
+ `Pigeon::NuntiumChannel` and `Pigeon::VerboiceChannel` to manipulate channels
66
+ which act as ActiveModels and provide a similar API to
67
+ [ActiveResource](https://github.com/rails/activeresource).
68
+
69
+ Pigeon channels should have a `kind` linking them to schemas which provide
70
+ information about the specific attributes required to configure each channel
71
+ type. The class methods `schemas` and `find_schema` provide access to all known
72
+ schemas.
73
+
74
+ Sample interaction session:
75
+
76
+ > c = Pigeon::NuntiumChannel.new name: 'foo', kind: 'pop3'
77
+ => #<Pigeon::NuntiumChannel:0xa45938c>
78
+ > c.schema.kind
79
+ => "pop3"
80
+ > c.attributes
81
+ => {"protocol"=>"mailto", "priority"=>100, "enabled"=>true, "direction"=>"bidirectional", "configuration"=>{}, "name"=>"foo", "kind"=>"pop3"}
82
+ > c.schema.user_attributes
83
+ => ["configuration[host]", "configuration[port]", "configuration[user]", "configuration[password]", "configuration[use_ssl]", "configuration[remove_quoted_text_or_text_after_first_empty_line]"]
84
+ > c.configuration[:host] = 'example.com'
85
+ => "example.com"
86
+ > c.assign_attributes('configuration[user]' => 'foo', 'configuration[password]' => 'bar')
87
+ => {"configuration[user]"=>"foo", "configuration[password]"=>"bar"}
88
+ > c.save
89
+ => false
90
+ > c.errors.full_messages
91
+ => ["port is not a number"]
92
+ > c.write_attribute('configuration[port]', 110)
93
+ => 110
94
+ > c.new_record?
95
+ => true
96
+ > c.save
97
+ => true
98
+ > Pigeon::NuntiumChannel.list
99
+ => ["foo"]
100
+ > c = Pigeon::NuntiumChannel.find('foo')
101
+ => #<Pigeon::NuntiumChannel:0xb027708>
102
+ > c.kind
103
+ => "pop3"
104
+ > c.new_record?
105
+ => false
106
+ > c.destroy
107
+ => true
108
+ > c.destroyed?
109
+ => true
110
+
111
+ The gem also provides helpers to aid in the rendering of the channel's
112
+ configuration form. The most important ones are
113
+ `pigeon_nuntium_channel_kinds_for_select` and
114
+ `pigeon_verboice_channel_kinds_for_select` to use as options generators for the
115
+ `select_tag` Rails helper, and `pigeon_render_channel` which will render
116
+ the fields required for a user to configure the given channel.
117
+
118
+ For example, in the view:
119
+
120
+ <%= form_tag('/channels') do %>
121
+ <%= hidden_field_tag :name, @channel.name %>
122
+ <%= pigeon_render_channel @channel %>
123
+ <%= end %>
124
+
125
+ Then in the controller:
126
+
127
+ def update
128
+ @channel = Pigeon::NuntiumChannel.find(params[:name])
129
+ @channel.assign_attributes(params[:channel])
130
+ @channel.save!
131
+ redirect_to channels_path
132
+ end
133
+
134
+ ## Contributing
135
+
136
+ 1. Fork it
137
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
138
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
139
+ 4. Push to the branch (`git push origin my-new-feature`)
140
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,58 @@
1
+ #= require pigeon/wizard
2
+
3
+ class QstServerWizard extends PigeonWizard
4
+ onPhoneType: (type) ->
5
+ @pages.each (index, page) ->
6
+ page_phone_type = $(page).data('phone-type')
7
+ if page_phone_type
8
+ page.disabled = page_phone_type != type
9
+ true
10
+ @attribute('phone_type').val(type)
11
+ @updateNavigation()
12
+
13
+ initPhoneType: (type) ->
14
+ @phone_type_radios.each (index, radio) ->
15
+ $(radio).attr('checked', radio.value == type)
16
+ @onPhoneType type
17
+
18
+ initWizard: ->
19
+ self = this
20
+
21
+ @navigation.show()
22
+ @pages[0].disabled = true
23
+
24
+ @ticket_code_fields = $('.ticket_code', @div)
25
+ @phone_type_radios = $('.phone_type', @div)
26
+
27
+ @initPhoneType @attribute('phone_type').val()
28
+ @phone_type_radios.click ->
29
+ self.onPhoneType @value
30
+
31
+ @ticket_code_fields.change ->
32
+ # update both ticket_code fields to make sure the current value gets
33
+ # posted
34
+ self.attribute('ticket_code').val(@value)
35
+
36
+ password_field = @attribute('configuration[password]')
37
+ if password_field.val() == ''
38
+ password_field.val(@generatePassword())
39
+
40
+ @attribute('ticket_message').val("This phone will be used for #{@app_name}.")
41
+ @attribute('use_ticket').val(true)
42
+
43
+ initPersisted: ->
44
+ $('#qst-reconfigure', @div).click =>
45
+ @initWizard()
46
+ @selectPage 1
47
+ @navigation.hide()
48
+ @selectPage 0
49
+
50
+ run: ->
51
+ if @persisted
52
+ @initPersisted()
53
+ else
54
+ @initWizard()
55
+ @selectDefaultPage()
56
+
57
+ PigeonWizard.registerClass 'qst-server', QstServerWizard
58
+
@@ -0,0 +1,43 @@
1
+ class PigeonTemplate
2
+ @klasses = {}
3
+
4
+ @registerClass: (type, klass) ->
5
+ @klasses[type] = klass
6
+
7
+ constructor: (@div) ->
8
+ div = $(@div)
9
+ @scope = div.data('scope')
10
+ @channel_name = div.data('name')
11
+ @channel_kind = div.data('kind')
12
+ @persisted = div.data('persisted')
13
+ @app_name = div.data('application-name') || 'Pigeon'
14
+
15
+ scoped_name: (name) ->
16
+ if @scope
17
+ bracket = name.indexOf('[')
18
+ if bracket >= 0
19
+ @scope + '[' + name[0..bracket-1] + ']' + name[bracket..-1]
20
+ else
21
+ @scope + '[' + name + ']'
22
+ else
23
+ name
24
+
25
+ attribute: (name) ->
26
+ $("[name=\"#{@scoped_name(name)}\"]", @div)
27
+
28
+ run: ->
29
+
30
+ window.PigeonTemplate = PigeonTemplate
31
+
32
+ jQuery.fn.pigeonTemplate = ->
33
+ @each (index, template) ->
34
+ type = $(template).data('type')
35
+ klass = PigeonTemplate.klasses[type]
36
+ if klass
37
+ new klass(template).run()
38
+ else
39
+ new PigeonTemplate(template).run()
40
+
41
+ jQuery ->
42
+ $('.pigeon.pigeon_template').pigeonTemplate()
43
+
@@ -0,0 +1,38 @@
1
+ #= require pigeon/template
2
+
3
+ class TwitterTemplate extends PigeonTemplate
4
+ onAuthorized: ->
5
+ $('.twitter_not_authorized').hide()
6
+ $('.twitter_authorized').show()
7
+ $('.screen_name').text(@screen_name.val())
8
+
9
+ beginTwitterAuthorization: ->
10
+ callback = 'pigeon_callback_' + (Math.round(Math.random() * 1000000))
11
+ window[callback] = (token, secret, screen_name) =>
12
+ @token.val(token)
13
+ @secret.val(secret)
14
+ @screen_name.val(screen_name)
15
+ @onAuthorized()
16
+
17
+ url = @twitter_auth_url + '?js_callback=' + callback
18
+ window.open(url, 'pigeon_twitter', 'menubar=no,location=yes,width=593,height=533')
19
+
20
+ run: ->
21
+ @screen_name = @attribute('configuration[screen_name]')
22
+ @token = @attribute('configuration[token]')
23
+ @secret = @attribute('configuration[secret]')
24
+
25
+ @button = $('.twitter_authorize_button')
26
+ @twitter_auth_url = $(@div).data('twitter-path')
27
+
28
+ if @screen_name.val() != ''
29
+ @onAuthorized()
30
+ else
31
+ $('.twitter_not_authorized').show()
32
+ $('.twitter_authorized').hide()
33
+
34
+ @button.click =>
35
+ @beginTwitterAuthorization()
36
+
37
+ PigeonTemplate.registerClass 'twitter', TwitterTemplate
38
+
@@ -0,0 +1,95 @@
1
+ #= require pigeon/template
2
+
3
+ class PigeonWizard extends PigeonTemplate
4
+ @klasses = {}
5
+
6
+ @registerClass: (type, klass) ->
7
+ @klasses[type] = klass
8
+
9
+ constructor: (@div) ->
10
+ super
11
+ @prevButton = $('<button type="button">Prev</button>')
12
+ @nextButton = $('<button type="button">Next</button>')
13
+ @pages = $(@div).find('.pigeon_wizard_page')
14
+ @navigation = $('<div class="pigeon_wizard_navigation"></div>').append(@prevButton).append(@nextButton).appendTo(@div)
15
+ @current = -1
16
+
17
+ @prevButton.click =>
18
+ prev = @prevActivePage(@current)
19
+ @selectPage prev if prev >= 0
20
+
21
+ @nextButton.click =>
22
+ next = @nextActivePage(@current)
23
+ @selectPage next if next >= 0
24
+
25
+ prevActivePage: (index) ->
26
+ index--
27
+ while index >= 0 && @pages[index].disabled
28
+ index--
29
+ index
30
+
31
+ nextActivePage: (index) ->
32
+ index++
33
+ while index < @pages.length && @pages[index].disabled
34
+ index++
35
+ if index >= @pages.length then -1 else index
36
+
37
+ selectPage: (index) ->
38
+ $(@pages[@current]).removeClass('active') unless @current < 0
39
+ $(@pages[index]).addClass('active')
40
+ @current = index
41
+ @updateNavigation()
42
+
43
+ updateNavigation: ->
44
+ if @prevActivePage(@current) < 0
45
+ @prevButton.attr('disabled', 'disabled')
46
+ else
47
+ @prevButton.removeAttr('disabled')
48
+ if @nextActivePage(@current) < 0
49
+ @nextButton.attr('disabled', 'disabled')
50
+ else
51
+ @nextButton.removeAttr('disabled')
52
+
53
+ selectDefaultPage: ->
54
+ # If any active page has a field with an error, select that
55
+ # Otherwise, select the first active page
56
+ pages_with_errors = $('.field_with_errors').
57
+ parents('.pigeon_wizard_page').filter -> !@disabled
58
+ found = -1
59
+ if pages_with_errors.length > 0
60
+ pages_with_errors.each (index, page) =>
61
+ index_in_pages = $.inArray(page, @pages)
62
+ if found < 0 || (index_in_pages >= 0 && index_in_pages < found)
63
+ found = index_in_pages
64
+
65
+ if found >= 0
66
+ @selectPage found
67
+ else
68
+ @selectPage @nextActivePage(-1)
69
+
70
+ run: ->
71
+ @selectDefaultPage()
72
+
73
+ generatePassword: (length = 8) ->
74
+ charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
75
+ retVal = ""
76
+ n = charset.length
77
+ for i in [0..length]
78
+ retVal += charset.charAt(Math.floor(Math.random() * n))
79
+ retVal
80
+
81
+
82
+ window.PigeonWizard = PigeonWizard
83
+
84
+ jQuery.fn.pigeonWizard = ->
85
+ @each (index, wizard) ->
86
+ type = $(wizard).data('type')
87
+ klass = PigeonWizard.klasses[type]
88
+ if klass
89
+ new klass(wizard).run()
90
+ else
91
+ new PigeonWizard(wizard).run()
92
+
93
+ jQuery ->
94
+ $('.pigeon.pigeon_wizard').pigeonWizard()
95
+
@@ -0,0 +1,2 @@
1
+ //= require_self
2
+ //= require_tree ./pigeon
@@ -0,0 +1,13 @@
1
+ .pigeon
2
+ &.pigeon_template
3
+ .pigeon_attribute
4
+ margin-top: 20px
5
+ input[type=text], input[type=password], input[type=email], input[type=url]
6
+ width: 300px
7
+
8
+ &.pigeon_wizard
9
+ .pigeon_wizard_page
10
+ display: none
11
+ &.active
12
+ display: block
13
+
@@ -0,0 +1,44 @@
1
+ require 'twitter_oauth'
2
+
3
+ module Pigeon
4
+ class TwitterController < ActionController::Base
5
+ def begin
6
+ client = new_twitter_client
7
+
8
+ request_token = client.request_token(:oauth_callback => twitter_callback_url)
9
+ session[:pigeon] ||= {}
10
+ session[:pigeon][:twitter] = {
11
+ token: request_token.token,
12
+ secret: request_token.secret
13
+ }
14
+ session[:pigeon][:js_callback] = params[:js_callback]
15
+ redirect_to request_token.authorize_url
16
+ end
17
+
18
+ def callback
19
+ if session[:pigeon].nil? || session[:pigeon][:twitter].nil?
20
+ redirect_to twitter_url
21
+ end
22
+
23
+ client = new_twitter_client
24
+
25
+ request_token = session[:pigeon][:twitter]
26
+ @access_token = client.authorize(
27
+ request_token[:token],
28
+ request_token[:secret],
29
+ :oauth_verifier => params[:oauth_verifier]
30
+ )
31
+ @callback = session[:pigeon][:js_callback] || 'pigeon_twitter_callback'
32
+ @screen_name = client.info['screen_name']
33
+ end
34
+
35
+ private
36
+
37
+ def new_twitter_client
38
+ client = TwitterOAuth::Client.new(
39
+ :consumer_key => Pigeon.config.twitter_consumer_key,
40
+ :consumer_secret => Pigeon.config.twitter_consumer_secret
41
+ )
42
+ end
43
+ end
44
+ end
File without changes
@@ -0,0 +1,93 @@
1
+ module Pigeon
2
+ module ChannelHelper
3
+ include ActionView::Helpers::CaptureHelper
4
+ include ActionView::Helpers::FormHelper
5
+ include ActionView::Helpers::FormOptionsHelper
6
+
7
+ def pigeon_attribute_field(attribute, value = nil, options = {})
8
+ scope = options.delete(:scope)
9
+ field_name = attribute.scoped_name(scope)
10
+ field_value = value || ''
11
+ forced_type = options.delete(:type)
12
+ render_type = (forced_type || attribute.type).to_s
13
+
14
+ case render_type
15
+ when "string"
16
+ text_field_tag(field_name, field_value, options)
17
+ when "password"
18
+ password_field_tag(field_name, field_value, options)
19
+ when "integer"
20
+ number_field_tag(field_name, field_value, options)
21
+ when "enum"
22
+ choices = attribute.options
23
+ if choices.length > 0 && choices[0].is_a?(Hash)
24
+ choices = choices.map { |h| [h["display"], h["value"]] }
25
+ end
26
+ select_tag(field_name, options_for_select(choices, field_value), options)
27
+ when "timezone"
28
+ select_tag(field_name, time_zone_options_for_select(field_value), options)
29
+ when "boolean"
30
+ check_box_tag(field_name, '1', field_value.present?, options)
31
+ when "hidden"
32
+ hidden_field_tag(field_name, field_value, options)
33
+ else
34
+ text_field_tag(field_name, field_value,
35
+ { :type => render_type }.merge(options))
36
+ end
37
+ end
38
+
39
+ def pigeon_attribute_label(attribute, options = {})
40
+ return '' if attribute.type == :hidden
41
+ scope = options.delete(:scope)
42
+ field_name = attribute.scoped_name(scope)
43
+ label_tag(field_name, attribute.label, options)
44
+ end
45
+
46
+ def pigeon_attribute(attribute, value = nil, options = {})
47
+ scope = options.delete(:scope)
48
+ field_with_errors = options.delete(:field_with_errors)
49
+ content_tag :div, options do
50
+ label = pigeon_attribute_label(attribute, scope: scope)
51
+ field = pigeon_attribute_field(attribute, value, scope: scope)
52
+ if field_with_errors.present?
53
+ field = content_tag :div, field, :class => 'field_with_errors'
54
+ end
55
+ if attribute.type == :boolean
56
+ safe_join([field, label])
57
+ elsif attribute.type == :hidden
58
+ field
59
+ else
60
+ safe_join([label, tag(:br), field])
61
+ end
62
+ end
63
+ end
64
+
65
+ def pigeon_channel_attribute_field(channel, attr_name, options = {})
66
+ value = channel.read_attribute(attr_name)
67
+ attribute = channel.schema.try(:find_attribute, attr_name)
68
+ attribute ||= ChannelAttribute.build_default(attr_name, value)
69
+ field = pigeon_attribute_field(attribute, value, options)
70
+ if channel.errors.include?(attr_name.to_sym)
71
+ content_tag :div, field, :class => 'field_with_errors'
72
+ else
73
+ field
74
+ end
75
+ end
76
+
77
+ def pigeon_channel_attribute_label(channel, attr_name, options = {})
78
+ attribute = channel.schema.try(:find_attribute, attr_name)
79
+ attribute ||= ChannelAttribute.build_default(attr_name)
80
+ pigeon_attribute_label(attribute, options)
81
+ end
82
+
83
+ def pigeon_channel_attribute(channel, attr_name, options = {})
84
+ value = channel.read_attribute(attr_name)
85
+ attribute = channel.schema.try(:find_attribute, attr_name)
86
+ attribute ||= ChannelAttribute.build_default(attr_name, value)
87
+ if channel.errors.include?(attr_name.to_sym)
88
+ options[:field_with_errors] = true
89
+ end
90
+ pigeon_attribute(attribute, value, options)
91
+ end
92
+ end
93
+ end