Rails, CoffeeScript - classでラップしてネームスペース管理

More than 3 years have passed since last update.


経緯


  • グローバルで変数名が衝突しないよう、ネームスペース・モジュール管理をやるべきらしい。

  • JSは多用しないので、可能であればRailsサイドで解決したい。

  • 今後は意識して実践していきたいのでメモ。


手法

調べてみるといろんな手法があったが、この方法が気に入った。


ネームスペース用のディレクトリーを作る(幾つでも良い)

/javascripts

/namespace # ネームスペース用のディレクトリー
base.coffee
charts.coffee
moving_items.coffee
[..]
application.js
namespace.js # ネームスペース設定ファイル


ネームスペースをアセットパイプラインに読み込んでもらう


/app/assets/javascripts/application.js

//= require jquery

//= require jquery_ujs
//= require jquery.turbolinks
//= require bootstrap
//= require turbolinks
//...
//= require namespace


/app/assets/javascripts/namespace.js

//= require ./namespace/base

//= require_tree ./namespace


baseファイルにネームスペース用オブジェクトを準備


/app/assets/javascripts/namespace/base.coffee

@Namespace = {}

# $(document).on "ready page:load", ->
# $('a[href^="/documents/"]').attr('target', '_blank')



JSコードをネームスペース配下のクラスでラップする

クラス内のコードはインスタンス化されるまで待機することになる


constructorで何かを戻り値として返す例


/app/assets/javascripts/namespace/charts.coffee

# Chart.js configuration

Chart.defaults.global.tooltipEvents = ["mousemove", "touchstart", "touchmove"]
Chart.defaults.global.scaleLabel = "<%=value%>cu.ft"

class @Components.ChartComponent

constructor: (chartType)->
return @createClass(chartType)

createClass: (chartType) ->

React.createClass
displayName: "#{chartType}Chart"
propTypes:
name: React.PropTypes.string
data: React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object])
height: React.PropTypes.number
width: React.PropTypes.number
options: React.PropTypes.object

getInitialState: ->
chartInstance: null

render: ->
React.DOM.canvas
ref: @props.name
style: { height: @props.height, width: @props.width }

componentDidMount: ->
@initializeChart()

componentWillUnmount: ->
@state.chartInstance.destroy() if @state.chartInstance

initializeChart: ->
canvas = React.findDOMNode(@refs[@props.name])
ctx = canvas.getContext("2d")
chart = new Chart(ctx)[chartType](@props.data)
@setState.chartInstance = chart



constructorでイベントリスナーを登録する例


/app/assets/javascripts/namespace/moving_items.coffee

# moving_items/new, moving_items/edit

class @Namespace.MovingItems

constructor: ->

setVolume = (volume) -> $("#moving_item_volume").val(volume)
setSlider = (volume) -> $("#volume_slider").val(volume)

# Slider

document.getElementById('volume_slider').addEventListener 'change', ->
setVolume(document.getElementById('volume_slider').value)

# AutoComplete

$('#moving_item_name').autocomplete
source: Object.keys( $('#suggestions').data('items') )
select: (e, ui) =>
itemVolume = $('#suggestions').data('items')[ui.item.value]
setVolume(itemVolume)
setSlider(itemVolume)

$('#moving_item_room').autocomplete
source: $('#suggestions').data('rooms')

$('#moving_item_category').autocomplete
source: $('#suggestions').data('categories')



HAMLの:coffeeフィルターを利用しJSコードを呼び出しインスタンス化


/app/views/movings/_chart_panel.html.haml

:coffee

window.BarChartComponent = new Namespace.ChartComponent("Bar")
window.PieChartComponent = new Namespace.ChartComponent("Pie")

.panel.panel-blue
.panel-heading
/...
- if @total_volume > 0
.panel-body
.row
.col-sm-6
= react_component 'BarChartComponent', { name: "MovingBarChart",
data: @dataForBarChart, height: 200, width: 400 }
.col-sm-6
= react_component 'PieChartComponent', { name: "MovingPieChart",
data: @dataForPieChart, height: 200, width: 200 }


同様のことが、content_for(:javascript)= yield(:javascript)でも可能。


/app/views/layouts/application.html.haml

!!!

%html
%head
/...
%body
= yield # ページコンテンツ
/...
= yield(:javascript) # 必要なJSコードを生成する


/app/views/movings/_chart_panel.html.haml

- content_for(:javascript) do

:coffeescript
jQuery ->
window.BarChartComponent = new Namespace.ChartComponent("Bar")
window.PieChartComponent = new Namespace.ChartComponent("Pie")
/...


資料